summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEmile Anclin <emile.anclin@logilab.fr>2008-09-08 17:07:36 +0200
committerEmile Anclin <emile.anclin@logilab.fr>2008-09-08 17:07:36 +0200
commit9156db779ab939a8b7c50f68a511887fd20057cc (patch)
tree4438021c61061fdf452a9ba63b74b7681fcc25f5
parent78ad977bb7fdb6b77232e16a6f25b36ee35755e7 (diff)
parentd6b4e6fb77a6743d3a27235837210e9d54757701 (diff)
downloadpylint-9156db779ab939a8b7c50f68a511887fd20057cc.tar.gz
merge pyreverse into pylint
-rw-r--r--.hgignore7
-rw-r--r--COPYING340
-rw-r--r--ChangeLog453
-rw-r--r--DEPENDS3
-rw-r--r--MANIFEST.in21
-rw-r--r--README59
-rw-r--r--TODO46
-rw-r--r--__init__.py16
-rw-r--r--__pkginfo__.py73
-rw-r--r--announce.txt42
-rwxr-xr-xbin/epylint29
-rwxr-xr-xbin/pylint4
-rwxr-xr-xbin/pylint-gui7
-rw-r--r--bin/pylint-gui.bat20
-rw-r--r--bin/pylint.bat19
-rwxr-xr-xbin/symilar3
-rw-r--r--bin/symilar.bat20
-rw-r--r--checkers/__init__.py164
-rw-r--r--checkers/base.py556
-rw-r--r--checkers/classes.py512
-rw-r--r--checkers/design_analysis.py330
-rw-r--r--checkers/exceptions.py154
-rw-r--r--checkers/format.py343
-rw-r--r--checkers/imports.py393
-rw-r--r--checkers/misc.py127
-rw-r--r--checkers/newstyle.py148
-rw-r--r--checkers/raw_metrics.py128
-rw-r--r--checkers/rpython.py448
-rw-r--r--checkers/similar.py334
-rw-r--r--checkers/typecheck.py202
-rw-r--r--checkers/utils.py181
-rw-r--r--checkers/variables.py444
-rw-r--r--config.py148
-rw-r--r--debian/NEWS.Debian10
-rw-r--r--debian/changelog318
-rw-r--r--debian/compat1
-rw-r--r--debian/control36
-rw-r--r--debian/copyright29
-rw-r--r--debian/pycompat1
-rw-r--r--debian/pylint.dirs2
-rw-r--r--debian/pylint.docs6
-rw-r--r--debian/pylint.emacsen-install45
-rw-r--r--debian/pylint.emacsen-remove14
-rw-r--r--debian/pylint.emacsen-startup17
-rw-r--r--debian/pylint.examples2
-rw-r--r--debian/pylint.manpages1
-rw-r--r--debian/pylint.postinst6
-rw-r--r--debian/pylint.postrm8
-rw-r--r--debian/pylint.prerm6
-rwxr-xr-xdebian/rules78
-rw-r--r--debian/watch3
-rw-r--r--doc/FAQ.txt186
-rw-r--r--doc/beginner_pylint_tutorial.txt376
-rw-r--r--doc/features.txt735
-rw-r--r--doc/makefile44
-rw-r--r--doc/manual.txt637
-rw-r--r--doc/quickstart.txt215
-rw-r--r--doc/rpython.txt30
-rw-r--r--elisp/pylint-flymake.el11
-rw-r--r--elisp/pylint.el60
-rw-r--r--elisp/startup17
-rw-r--r--examples/custom.py38
-rw-r--r--examples/custom_raw.py31
-rw-r--r--examples/pylintrc309
-rw-r--r--examples/pylintrc_camelcase24
-rw-r--r--gui.py82
-rw-r--r--interfaces.py98
-rw-r--r--lint.py957
-rw-r--r--man/pylint.1252
-rw-r--r--reporters/__init__.py67
-rw-r--r--reporters/html.py65
-rw-r--r--reporters/text.py156
-rw-r--r--setup.cfg3
-rw-r--r--setup.py188
-rwxr-xr-xtest/fulltest.sh17
-rw-r--r--test/func_test.py219
-rw-r--r--test/func_test_rpython.py114
-rw-r--r--test/func_test_sample_config.py31
-rw-r--r--test/genbuiltins.py63
-rw-r--r--test/input/__init__.py1
-rw-r--r--test/input/func___future___import_not_first_stmt.py5
-rw-r--r--test/input/func___name___access.py21
-rw-r--r--test/input/func_attrs_definition_order.py15
-rw-r--r--test/input/func_backtick_deprecated.py4
-rw-r--r--test/input/func_bad_assigment_to_exception_var.py31
-rw-r--r--test/input/func_base_stmt_without_effect.py28
-rw-r--r--test/input/func_base_useless_pass.py9
-rw-r--r--test/input/func_block_disable_msg.py1017
-rw-r--r--test/input/func_class_access_protected_members.py30
-rw-r--r--test/input/func_class_members.py31
-rw-r--r--test/input/func_continue_not_in_loop.py14
-rw-r--r--test/input/func_dangerous_default.py17
-rw-r--r--test/input/func_docstring.py48
-rw-r--r--test/input/func_dotted_ancestor.py11
-rw-r--r--test/input/func_e0011.py5
-rw-r--r--test/input/func_e0012.py5
-rw-r--r--test/input/func_e0101.py29
-rw-r--r--test/input/func_e0203.py19
-rw-r--r--test/input/func_e0204.py19
-rw-r--r--test/input/func_e0205.py17
-rw-r--r--test/input/func_e0206.py20
-rw-r--r--test/input/func_e0214.py18
-rw-r--r--test/input/func_e0601.py9
-rw-r--r--test/input/func_empty_module.py0
-rw-r--r--test/input/func_exceptions_raise_type_error.py14
-rw-r--r--test/input/func_f0001.py4
-rw-r--r--test/input/func_f0401.py9
-rw-r--r--test/input/func_fixme.py9
-rw-r--r--test/input/func_format.py64
-rw-r--r--test/input/func_genexpr_var_scope_py24.py6
-rw-r--r--test/input/func_globals.py40
-rw-r--r--test/input/func_i0010.py3
-rw-r--r--test/input/func_i0011.py5
-rw-r--r--test/input/func_i0012.py5
-rw-r--r--test/input/func_i0013.py8
-rw-r--r--test/input/func_indent.py22
-rw-r--r--test/input/func_init_vars.py46
-rw-r--r--test/input/func_interfaces.py99
-rw-r--r--test/input/func_method_could_be_function.py52
-rw-r--r--test/input/func_method_missing_self.py25
-rw-r--r--test/input/func_method_without_self_but_self_assignment.py15
-rw-r--r--test/input/func_nameerror_on_string_substitution.py8
-rw-r--r--test/input/func_names_imported_from_module.py31
-rw-r--r--test/input/func_newstyle___slots__.py17
-rw-r--r--test/input/func_newstyle_exceptions.py35
-rw-r--r--test/input/func_newstyle_property.py19
-rw-r--r--test/input/func_newstyle_super.py22
-rw-r--r--test/input/func_noerror___future___import.py5
-rw-r--r--test/input/func_noerror___init___return_from_inner_function.py13
-rw-r--r--test/input/func_noerror_access_attr_before_def_false_positive.py100
-rw-r--r--test/input/func_noerror_base_init_vars.py36
-rw-r--r--test/input/func_noerror_builtin_module_test.py11
-rw-r--r--test/input/func_noerror_classes_meth_could_be_a_function.py34
-rw-r--r--test/input/func_noerror_classes_meth_signature.py12
-rw-r--r--test/input/func_noerror_classes_protected_member_access.py24
-rw-r--r--test/input/func_noerror_defined_and_used_on_same_line.py23
-rw-r--r--test/input/func_noerror_defined_and_used_on_same_line_py24.py7
-rw-r--r--test/input/func_noerror_e1101_13784.py15
-rw-r--r--test/input/func_noerror_e1101_but_getattr.py23
-rw-r--r--test/input/func_noerror_encoding.py6
-rw-r--r--test/input/func_noerror_except_pass.py11
-rw-r--r--test/input/func_noerror_exception.py7
-rw-r--r--test/input/func_noerror_external_classmethod_crash.py21
-rw-r--r--test/input/func_noerror_factory_method.py23
-rw-r--r--test/input/func_noerror_indirect_interface.py16
-rw-r--r--test/input/func_noerror_inner_classes.py33
-rw-r--r--test/input/func_noerror_mcs_attr_access.py20
-rw-r--r--test/input/func_noerror_nested_classes.py18
-rw-r--r--test/input/func_noerror_new_style_class.py45
-rw-r--r--test/input/func_noerror_nonregr.py13
-rw-r--r--test/input/func_noerror_object_as_class_attribute.py19
-rw-r--r--test/input/func_noerror_overloaded_operator.py21
-rw-r--r--test/input/func_noerror_socket_member.py25
-rw-r--r--test/input/func_noerror_static_method.py29
-rw-r--r--test/input/func_noerror_staticmethod_as_decorator.py35
-rw-r--r--test/input/func_noerror_w0232.py10
-rw-r--r--test/input/func_noerror_yield_return_mix.py7
-rw-r--r--test/input/func_nonascii_noencoding.py5
-rw-r--r--test/input/func_r0901.py27
-rw-r--r--test/input/func_r0902.py28
-rw-r--r--test/input/func_r0903.py13
-rw-r--r--test/input/func_r0904.py73
-rw-r--r--test/input/func_r0921.py15
-rw-r--r--test/input/func_r0922.py21
-rw-r--r--test/input/func_r0923.py32
-rw-r--r--test/input/func_reqattrs.py1
-rw-r--r--test/input/func_return_outside_func.py3
-rw-r--r--test/input/func_return_yield_mix.py8
-rw-r--r--test/input/func_return_yield_mix2.py8
-rw-r--r--test/input/func_scope_regrtest.py15
-rw-r--r--test/input/func_syntax_error.py1
-rw-r--r--test/input/func_toolonglines.py4
-rw-r--r--test/input/func_typecheck_callfunc_assigment.py56
-rw-r--r--test/input/func_typecheck_getattr.py66
-rw-r--r--test/input/func_typecheck_non_callable_call.py37
-rw-r--r--test/input/func_undefined_var.py26
-rw-r--r--test/input/func_unknown_encoding.py6
-rw-r--r--test/input/func_unreachable.py22
-rw-r--r--test/input/func_use_for_or_listcomp_var.py21
-rw-r--r--test/input/func_variables_unused_name_from_wilcard_import.py3
-rw-r--r--test/input/func_w0101.py28
-rw-r--r--test/input/func_w0102.py53
-rw-r--r--test/input/func_w0103.py8
-rw-r--r--test/input/func_w0104.py12
-rw-r--r--test/input/func_w0105.py62
-rw-r--r--test/input/func_w0109.py7
-rw-r--r--test/input/func_w0110.py10
-rw-r--r--test/input/func_w0111.py10
-rw-r--r--test/input/func_w0112.py37
-rw-r--r--test/input/func_w0122.py13
-rw-r--r--test/input/func_w0133.py54
-rw-r--r--test/input/func_w0151.py7
-rw-r--r--test/input/func_w0152.py14
-rw-r--r--test/input/func_w0202.py17
-rw-r--r--test/input/func_w0205.py24
-rw-r--r--test/input/func_w0223.py27
-rw-r--r--test/input/func_w0231.py38
-rw-r--r--test/input/func_w0233.py28
-rw-r--r--test/input/func_w0302.py1016
-rw-r--r--test/input/func_w0312.py11
-rw-r--r--test/input/func_w0331.py7
-rw-r--r--test/input/func_w0332.py4
-rw-r--r--test/input/func_w0401.py9
-rw-r--r--test/input/func_w0402.py9
-rw-r--r--test/input/func_w0403.py12
-rw-r--r--test/input/func_w0404.py10
-rw-r--r--test/input/func_w0405.py31
-rw-r--r--test/input/func_w0406.py9
-rw-r--r--test/input/func_w0611.py22
-rw-r--r--test/input/func_w0612.py8
-rw-r--r--test/input/func_w0613.py18
-rw-r--r--test/input/func_w0622.py11
-rw-r--r--test/input/func_w0701.py12
-rw-r--r--test/input/func_w0702.py17
-rw-r--r--test/input/func_w0703.py9
-rw-r--r--test/input/func_w0704.py16
-rw-r--r--test/input/func_w0705.py46
-rw-r--r--test/input/func_w0801.py11
-rw-r--r--test/input/func_wrong_encoding.py6
-rw-r--r--test/input/func_yield_outside_func.py4
-rw-r--r--test/input/indirect1.py4
-rw-r--r--test/input/indirect2.py7
-rw-r--r--test/input/indirect3.py5
-rw-r--r--test/input/noext4
-rw-r--r--test/input/similar119
-rw-r--r--test/input/similar219
-rw-r--r--test/input/w0401_cycle.py9
-rw-r--r--test/input/w0801_same.py11
-rw-r--r--test/messages/2.5_func_newstyle_exceptions.txt4
-rw-r--r--test/messages/builtin_module.txt1
-rw-r--r--test/messages/func___future___import_not_first_stmt.txt1
-rw-r--r--test/messages/func___name___access.txt3
-rw-r--r--test/messages/func_attrs_definition_order.txt1
-rw-r--r--test/messages/func_backtick_deprecated.txt2
-rw-r--r--test/messages/func_bad_assigment_to_exception_var.txt5
-rw-r--r--test/messages/func_base_stmt_without_effect.txt6
-rw-r--r--test/messages/func_base_useless_pass.txt1
-rw-r--r--test/messages/func_block_disable_msg.txt10
-rw-r--r--test/messages/func_class_access_protected_members.txt3
-rw-r--r--test/messages/func_class_members.txt3
-rw-r--r--test/messages/func_continue_not_in_loop.txt2
-rw-r--r--test/messages/func_dangerous_default.txt2
-rw-r--r--test/messages/func_docstring.txt4
-rw-r--r--test/messages/func_dotted_ancestor.txt1
-rw-r--r--test/messages/func_e0011.txt1
-rw-r--r--test/messages/func_e0012.txt1
-rw-r--r--test/messages/func_e0101.txt2
-rw-r--r--test/messages/func_e0203.txt2
-rw-r--r--test/messages/func_e0204.txt3
-rw-r--r--test/messages/func_e0205.txt2
-rw-r--r--test/messages/func_e0206.txt3
-rw-r--r--test/messages/func_e0214.txt2
-rw-r--r--test/messages/func_e0601.txt1
-rw-r--r--test/messages/func_empty_module.txt2
-rw-r--r--test/messages/func_exceptions_raise_type_error.txt2
-rw-r--r--test/messages/func_f0001.txt1
-rw-r--r--test/messages/func_f0401.txt2
-rw-r--r--test/messages/func_fixme.txt2
-rw-r--r--test/messages/func_format.txt25
-rw-r--r--test/messages/func_genexpr_var_scope_py24.txt1
-rw-r--r--test/messages/func_globals.txt6
-rw-r--r--test/messages/func_i0010.txt1
-rw-r--r--test/messages/func_i0011.txt2
-rw-r--r--test/messages/func_i0012.txt2
-rw-r--r--test/messages/func_i0013.txt1
-rw-r--r--test/messages/func_indent.txt3
-rw-r--r--test/messages/func_init_vars.txt1
-rw-r--r--test/messages/func_interfaces.txt6
-rw-r--r--test/messages/func_method_could_be_function.txt1
-rw-r--r--test/messages/func_method_missing_self.txt1
-rw-r--r--test/messages/func_method_without_self_but_self_assignment.txt2
-rw-r--r--test/messages/func_nameerror_on_string_substitution.txt2
-rw-r--r--test/messages/func_names_imported_from_module.txt8
-rw-r--r--test/messages/func_newstyle___slots__.txt1
-rw-r--r--test/messages/func_newstyle_exceptions.txt4
-rw-r--r--test/messages/func_newstyle_property.txt1
-rw-r--r--test/messages/func_newstyle_super.txt4
-rw-r--r--test/messages/func_nonascii_noencoding.txt1
-rw-r--r--test/messages/func_r0901.txt2
-rw-r--r--test/messages/func_r0902.txt1
-rw-r--r--test/messages/func_r0903.txt1
-rw-r--r--test/messages/func_r0904.txt1
-rw-r--r--test/messages/func_r0921.txt1
-rw-r--r--test/messages/func_r0922.txt1
-rw-r--r--test/messages/func_r0923.txt1
-rw-r--r--test/messages/func_reqattrs.txt1
-rw-r--r--test/messages/func_return_outside_func.txt1
-rw-r--r--test/messages/func_return_yield_mix.txt1
-rw-r--r--test/messages/func_return_yield_mix2.txt1
-rw-r--r--test/messages/func_scope_regrtest.txt2
-rw-r--r--test/messages/func_syntax_error.txt2
-rw-r--r--test/messages/func_toolonglines.txt2
-rw-r--r--test/messages/func_typecheck_callfunc_assigment.txt2
-rw-r--r--test/messages/func_typecheck_getattr.txt10
-rw-r--r--test/messages/func_typecheck_non_callable_call.txt6
-rw-r--r--test/messages/func_undefined_var.txt5
-rw-r--r--test/messages/func_unknown_encoding.txt1
-rw-r--r--test/messages/func_unreachable.txt3
-rw-r--r--test/messages/func_use_for_or_listcomp_var.txt3
-rw-r--r--test/messages/func_variables_unused_name_from_wilcard_import.txt5
-rw-r--r--test/messages/func_w0101.txt1
-rw-r--r--test/messages/func_w0102.txt5
-rw-r--r--test/messages/func_w0103.txt1
-rw-r--r--test/messages/func_w0104.txt1
-rw-r--r--test/messages/func_w0105.txt1
-rw-r--r--test/messages/func_w0109.txt1
-rw-r--r--test/messages/func_w0110.txt1
-rw-r--r--test/messages/func_w0111.txt1
-rw-r--r--test/messages/func_w0112.txt1
-rw-r--r--test/messages/func_w0122.txt5
-rw-r--r--test/messages/func_w0133.txt9
-rw-r--r--test/messages/func_w0151.txt2
-rw-r--r--test/messages/func_w0152.txt3
-rw-r--r--test/messages/func_w0202.txt3
-rw-r--r--test/messages/func_w0205.txt2
-rw-r--r--test/messages/func_w0223.txt2
-rw-r--r--test/messages/func_w0231.txt2
-rw-r--r--test/messages/func_w0233.txt1
-rw-r--r--test/messages/func_w0302.txt2
-rw-r--r--test/messages/func_w0312.txt2
-rw-r--r--test/messages/func_w0331.txt1
-rw-r--r--test/messages/func_w0332.txt1
-rw-r--r--test/messages/func_w0401.txt2
-rw-r--r--test/messages/func_w0402.txt2
-rw-r--r--test/messages/func_w0403.txt1
-rw-r--r--test/messages/func_w0404.txt1
-rw-r--r--test/messages/func_w0405.txt4
-rw-r--r--test/messages/func_w0406.txt2
-rw-r--r--test/messages/func_w0611.txt1
-rw-r--r--test/messages/func_w0612.txt1
-rw-r--r--test/messages/func_w0613.txt2
-rw-r--r--test/messages/func_w0622.txt2
-rw-r--r--test/messages/func_w0701.txt3
-rw-r--r--test/messages/func_w0702.txt1
-rw-r--r--test/messages/func_w0703.txt1
-rw-r--r--test/messages/func_w0704.txt1
-rw-r--r--test/messages/func_w0705.txt5
-rw-r--r--test/messages/func_w0801.txt11
-rw-r--r--test/messages/func_wrong_encoding.txt1
-rw-r--r--test/messages/func_yield_outside_func.txt1
-rw-r--r--test/messages/nonexistant.txt1
-rw-r--r--test/messages/nonexistant.txt21
-rw-r--r--test/regrtest.py142
-rw-r--r--test/regrtest_data/application_crash.py12
-rw-r--r--test/regrtest_data/classdoc_usage.py17
-rw-r--r--test/regrtest_data/decimal_inference.py10
-rw-r--r--test/regrtest_data/descriptor_crash.py20
-rw-r--r--test/regrtest_data/import_package_subpackage_module.py49
-rw-r--r--test/regrtest_data/module_global.py7
-rw-r--r--test/regrtest_data/numarray_import.py7
-rw-r--r--test/regrtest_data/numarray_inf.py5
-rw-r--r--test/regrtest_data/package/AudioTime.py3
-rw-r--r--test/regrtest_data/package/__init__.py14
-rw-r--r--test/regrtest_data/package/subpackage/__init__.py1
-rw-r--r--test/regrtest_data/package/subpackage/module.py1
-rw-r--r--test/regrtest_data/precedence_test.py21
-rw-r--r--test/regrtest_data/pygtk_import.py14
-rw-r--r--test/regrtest_data/socketerror_import.py6
-rw-r--r--test/regrtest_data/try_finally_disable_msg_crash.py5
-rw-r--r--test/rpythoninput/__init__.py1
-rw-r--r--test/rpythoninput/func_genexpr.py11
-rw-r--r--test/rpythoninput/func_immutable_global.py15
-rw-r--r--test/rpythoninput/func_immutable_global1.py14
-rw-r--r--test/rpythoninput/func_immutable_global2.py15
-rw-r--r--test/rpythoninput/func_immutable_global3.py15
-rw-r--r--test/rpythoninput/func_multiple_inheritance.py20
-rw-r--r--test/rpythoninput/func_multiple_inheritance2.py22
-rw-r--r--test/rpythoninput/func_multiple_types_assignment.py30
-rw-r--r--test/rpythoninput/func_nobuiltin_buffer.py13
-rw-r--r--test/rpythoninput/func_nobuiltin_callable.py13
-rw-r--r--test/rpythoninput/func_nobuiltin_classmethod.py13
-rw-r--r--test/rpythoninput/func_nobuiltin_compile.py13
-rw-r--r--test/rpythoninput/func_nobuiltin_complex.py13
-rw-r--r--test/rpythoninput/func_nobuiltin_delattr.py13
-rw-r--r--test/rpythoninput/func_nobuiltin_dict.py13
-rw-r--r--test/rpythoninput/func_nobuiltin_dir.py13
-rw-r--r--test/rpythoninput/func_nobuiltin_enumerate.py13
-rw-r--r--test/rpythoninput/func_nobuiltin_eval.py13
-rw-r--r--test/rpythoninput/func_nobuiltin_execfile.py13
-rw-r--r--test/rpythoninput/func_nobuiltin_file.py13
-rw-r--r--test/rpythoninput/func_nobuiltin_filter.py13
-rw-r--r--test/rpythoninput/func_nobuiltin_frozenset.py13
-rw-r--r--test/rpythoninput/func_nobuiltin_getattr.py13
-rw-r--r--test/rpythoninput/func_nobuiltin_globals.py13
-rw-r--r--test/rpythoninput/func_nobuiltin_help.py13
-rw-r--r--test/rpythoninput/func_nobuiltin_id.py13
-rw-r--r--test/rpythoninput/func_nobuiltin_input.py13
-rw-r--r--test/rpythoninput/func_nobuiltin_intern.py13
-rw-r--r--test/rpythoninput/func_nobuiltin_issubclass.py13
-rw-r--r--test/rpythoninput/func_nobuiltin_iter.py13
-rw-r--r--test/rpythoninput/func_nobuiltin_locals.py13
-rw-r--r--test/rpythoninput/func_nobuiltin_map.py13
-rw-r--r--test/rpythoninput/func_nobuiltin_object.py13
-rw-r--r--test/rpythoninput/func_nobuiltin_open.py13
-rw-r--r--test/rpythoninput/func_nobuiltin_property.py13
-rw-r--r--test/rpythoninput/func_nobuiltin_raw_input.py13
-rw-r--r--test/rpythoninput/func_nobuiltin_reduce.py13
-rw-r--r--test/rpythoninput/func_nobuiltin_reload.py13
-rw-r--r--test/rpythoninput/func_nobuiltin_reversed.py13
-rw-r--r--test/rpythoninput/func_nobuiltin_round.py13
-rw-r--r--test/rpythoninput/func_nobuiltin_set.py13
-rw-r--r--test/rpythoninput/func_nobuiltin_setattr.py13
-rw-r--r--test/rpythoninput/func_nobuiltin_sorted.py13
-rw-r--r--test/rpythoninput/func_nobuiltin_staticmethod.py13
-rw-r--r--test/rpythoninput/func_nobuiltin_sum.py13
-rw-r--r--test/rpythoninput/func_nobuiltin_super.py13
-rw-r--r--test/rpythoninput/func_nobuiltin_unicode.py13
-rw-r--r--test/rpythoninput/func_nobuiltin_vars.py13
-rw-r--r--test/rpythoninput/func_noerror_multiple_inheritance.py25
-rw-r--r--test/rpythoninput/func_noerror_mutable_global.py18
-rw-r--r--test/rpythoninput/func_noerror_notrpython.py12
-rw-r--r--test/rpythoninput/func_noerror_slice_index.py17
-rw-r--r--test/rpythoninput/func_non_homogeneous_list.py12
-rw-r--r--test/rpythoninput/func_noyield.py17
-rw-r--r--test/rpythoninput/func_repr_format_string.py12
-rw-r--r--test/rpythoninput/func_slice_negative_index.py20
-rw-r--r--test/rpythoninput/func_slice_non_constant_step.py12
-rw-r--r--test/rpythoninput/func_unsupported_protocol.py23
-rw-r--r--test/rpythoninput/immutable_global3.py1
-rw-r--r--test/rpythonmessages/func_genexpr.txt1
-rw-r--r--test/rpythonmessages/func_immutable_global.txt1
-rw-r--r--test/rpythonmessages/func_immutable_global1.txt1
-rw-r--r--test/rpythonmessages/func_immutable_global2.txt1
-rw-r--r--test/rpythonmessages/func_immutable_global3.txt2
-rw-r--r--test/rpythonmessages/func_multiple_inheritance.txt1
-rw-r--r--test/rpythonmessages/func_multiple_inheritance2.txt1
-rw-r--r--test/rpythonmessages/func_multiple_types_assignment.txt2
-rw-r--r--test/rpythonmessages/func_nobuiltin_buffer.txt1
-rw-r--r--test/rpythonmessages/func_nobuiltin_callable.txt1
-rw-r--r--test/rpythonmessages/func_nobuiltin_classmethod.txt1
-rw-r--r--test/rpythonmessages/func_nobuiltin_compile.txt1
-rw-r--r--test/rpythonmessages/func_nobuiltin_complex.txt1
-rw-r--r--test/rpythonmessages/func_nobuiltin_delattr.txt1
-rw-r--r--test/rpythonmessages/func_nobuiltin_dict.txt1
-rw-r--r--test/rpythonmessages/func_nobuiltin_dir.txt1
-rw-r--r--test/rpythonmessages/func_nobuiltin_enumerate.txt1
-rw-r--r--test/rpythonmessages/func_nobuiltin_eval.txt1
-rw-r--r--test/rpythonmessages/func_nobuiltin_execfile.txt1
-rw-r--r--test/rpythonmessages/func_nobuiltin_file.txt1
-rw-r--r--test/rpythonmessages/func_nobuiltin_filter.txt1
-rw-r--r--test/rpythonmessages/func_nobuiltin_frozenset.txt1
-rw-r--r--test/rpythonmessages/func_nobuiltin_getattr.txt1
-rw-r--r--test/rpythonmessages/func_nobuiltin_globals.txt1
-rw-r--r--test/rpythonmessages/func_nobuiltin_help.txt1
-rw-r--r--test/rpythonmessages/func_nobuiltin_id.txt1
-rw-r--r--test/rpythonmessages/func_nobuiltin_input.txt1
-rw-r--r--test/rpythonmessages/func_nobuiltin_intern.txt1
-rw-r--r--test/rpythonmessages/func_nobuiltin_issubclass.txt1
-rw-r--r--test/rpythonmessages/func_nobuiltin_iter.txt1
-rw-r--r--test/rpythonmessages/func_nobuiltin_locals.txt1
-rw-r--r--test/rpythonmessages/func_nobuiltin_map.txt1
-rw-r--r--test/rpythonmessages/func_nobuiltin_object.txt1
-rw-r--r--test/rpythonmessages/func_nobuiltin_open.txt1
-rw-r--r--test/rpythonmessages/func_nobuiltin_property.txt1
-rw-r--r--test/rpythonmessages/func_nobuiltin_raw_input.txt1
-rw-r--r--test/rpythonmessages/func_nobuiltin_reduce.txt1
-rw-r--r--test/rpythonmessages/func_nobuiltin_reload.txt1
-rw-r--r--test/rpythonmessages/func_nobuiltin_reversed.txt1
-rw-r--r--test/rpythonmessages/func_nobuiltin_round.txt1
-rw-r--r--test/rpythonmessages/func_nobuiltin_set.txt1
-rw-r--r--test/rpythonmessages/func_nobuiltin_setattr.txt1
-rw-r--r--test/rpythonmessages/func_nobuiltin_sorted.txt1
-rw-r--r--test/rpythonmessages/func_nobuiltin_staticmethod.txt1
-rw-r--r--test/rpythonmessages/func_nobuiltin_sum.txt1
-rw-r--r--test/rpythonmessages/func_nobuiltin_super.txt1
-rw-r--r--test/rpythonmessages/func_nobuiltin_unicode.txt1
-rw-r--r--test/rpythonmessages/func_nobuiltin_vars.txt1
-rw-r--r--test/rpythonmessages/func_non_homogeneous_list.txt1
-rw-r--r--test/rpythonmessages/func_noyield.txt3
-rw-r--r--test/rpythonmessages/func_repr_format_string.txt2
-rw-r--r--test/rpythonmessages/func_slice_negative_index.txt9
-rw-r--r--test/rpythonmessages/func_slice_non_constant_step.txt1
-rw-r--r--test/rpythonmessages/func_unsupported_protocol.txt2
-rw-r--r--test/runtests.py5
-rw-r--r--test/smoketest.py71
-rw-r--r--test/test_encoding.py62
-rw-r--r--test/test_format.py168
-rw-r--r--test/test_import_graph.py62
-rw-r--r--test/test_rpycompilation.py50
-rw-r--r--test/test_similar.py56
-rw-r--r--test/unittest_checkers_utils.py49
-rw-r--r--test/unittest_lint.py316
-rw-r--r--utils.py387
483 files changed, 19094 insertions, 0 deletions
diff --git a/.hgignore b/.hgignore
new file mode 100644
index 0000000..73667ad
--- /dev/null
+++ b/.hgignore
@@ -0,0 +1,7 @@
+(^|/)\.svn($|/)
+(^|/)\.hg($|/)
+(^|/)\.hgtags($|/)
+^log$
+\.pyc$
+\.pyo$
+^build$
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..b7b5f53
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,340 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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 St, Fifth Floor, Boston, MA 02110-1301 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/ChangeLog b/ChangeLog
new file mode 100644
index 0000000..1baa459
--- /dev/null
+++ b/ChangeLog
@@ -0,0 +1,453 @@
+ChangeLog for PyLint
+====================
+
+-- 0.15.0
+ * included Stefan Rank's patch to deal with 2.4 relative import
+ * included Robert Kirkpatrick's tutorial and typos fixes
+ * fix bug in reenabling message
+ * fix #2473: invoking pylint on __init__.py (hopefuly)
+ * typecheck: acquired-members option has been dropped in favor of the more
+ generic generated-members option. If the zope option is set, the behaviour
+ is now to add some default values to generated-members.
+ * flymake integration: added bin/epylint and elisp/pylint-flymake.el
+
+2008-01-14 -- 0.14.0
+ * fix #3733: Messages (dis)appear depending on order of file names
+ * fix #4026: pylint.el should require compile
+ * fix a bug in colorized reporter, spotted by Dave Borowitz
+ * applied patch from Stefan Rank to avoid W0410 false positive when
+ multiple "from __future__" import statements
+ * implement #4012: flag back tick as deprecated (new W0333 message)
+ * new ignored-class option on typecheck checker allowing to skip members
+ checking based on class name (patch provided by Thomas W Barr)
+
+
+2007-06-07 -- 0.13.2
+ * fix disable-checker option so that it won't accidentally enable the
+ rpython checker which is disabled by default
+ * added note about the gedit plugin into documentation
+
+2007-03-02 -- 0.13.1
+ * fix some unexplained 0.13.0 packaging issue which led to a bunch of
+ files missing from the distribution
+
+2007-02-28 -- 0.13.0
+ * new RPython (Restricted Python) checker for PyPy felow or people
+ wanting to get a compiled version of their python program using the
+ translator of the PyPy project. For more information about PyPy or
+ RPython, visit http://codespeak.net/pypy/
+ * new E0104 and E0105 messages introduced to respectivly warn about
+ "return" and "yield" outside function or method
+ * new E0106 message when "yield" and "return something" are mixed in a
+ function or method
+ * new W0107 message for unnecessary pass statement
+ * new W0614 message to differentiate between unused `import X` and
+ unused `from X import *` (#3209, patch submitted by Daniel Drake)
+ * included Daniel Drake's patch to have a different message E1003 instead of
+ E1001 when a missing member is found but an inference failure has been
+ detected
+ * msvs reporter for Visual Studio line number reporting (#3285)
+ * allow disable-all option inline (#3218, patch submitted by Daniel Drake)
+ * --init-hook option to call arbitray code necessary to set
+ environment (eg sys.path) (#3156)
+ * One more Daniel's patch fixing a command line option parsing
+ problem, this'll definitly be the DDrake release :)
+ * fix #3184: crashes on "return" outside function
+ * fix #3205: W0704 false positive
+ * fix #3123: W0212 false positive on static method
+ * fix #2485: W0222 false positive
+ * fix #3259: when a message is explicitly enabled, check the checker
+ emitting it is enabled
+
+
+2006-11-23 -- 0.12.2
+ * fix #3143: W0233 bug w/ YES objects
+ * fix #3119: Off-by-one error counting lines in a file
+ * fix #3117: ease sys.stdout overriding for reporters
+ * fix #2508: E0601 false positive with lambda
+ * fix #3125: E1101 false positive and a message duplication. Only the last part
+ is actually fixed since the initial false positive is due to dynaming setting of
+ attributes on the decimal.Context class.
+ * fix #3149: E0101 false positives and introduced E0100 for generator __init__
+ methods
+ * fixed some format checker false positives
+
+2006-09-25 -- 0.12.1
+ * fixed python >= 2.4 format false positive with multiple lines statement
+ * fixed some 2.5 issues
+ * fixed generator expression scope bug (depends on astng 0.16.1)
+ * stop requiring __revision__
+
+2006-08-10 -- 0.12.0
+ * usability changes:
+
+ - parseable, html and color options are now handled by a single
+ output-format option
+ - enable-<checkerid> and disable-all options are now handled by
+ two (exclusive) enable-checker and disable-checker options
+ taking a comma separated list of checker names as value
+ - renamed debug-mode option to errors-only
+
+ * started a reference user manual
+ * new W0212 message for access to protected member from client code
+ (close #14081)
+ * new W0105 and W0106 messages extracted from W0104 (statement seems
+ to have no effect) respectivly when the statement is actually string
+ (that's sometimes used instead of comments for documentation) or an
+ empty statement generated by a useless semicolumn
+ * reclassified W0302 to C0302
+ * fix so that global messages are not anymore connected to the last
+ analyzed module (close #10106)
+ * fix some bugs related to local disabling of messages
+ * fix cr/lf pb when generating the rc file on windows platforms
+
+
+2006-04-19 -- 0.11.0
+ * fix crash caused by the exceptions checker in some case
+ * fix some E1101 false positive with abstract method or classes defining
+ __getattr__
+ * dirty fix to avoid "_socketobject" has not "connect" member. The actual
+ problem is that astng isn't able to understand the code used to create
+ socket.socket object with exec
+ * added an option in the similarity checker to ignore docstrings, enabled
+ by default
+ * included patch from Benjamin Niemann to allow block level
+ enabling/disabling of messages
+
+
+2006-03-06 -- 0.10.0
+ * WARNING, this release include some configuration changes (see below),
+ so you may have to check and update your own configuration file(s) if
+ you use one
+ * this release require the 0.15 version of astng or superior (it will save
+ you a lot of pylint crashes...)
+ * W0705 has been reclassified to E0701, and is now detecting more
+ inheriting problem, and a false positive when empty except clause is
+ following an Exception catch has been fixed (close #10422)
+ * E0212 and E0214 (metaclass/class method should have mcs/cls as first
+ argument have been reclassified to C0202 and C0203 since this not as
+ well established as "self" for instance method (E0213)
+ * W0224 has been reclassified into F0220 (failed to resolve interfaces
+ implemented by a class)
+ * a new typecheck checker, introducing the following checks:
+
+ - E1101, access to unexistant member (implements #10430), remove
+ the need of E0201 and so some options has been moved from the
+ classes checker to this one
+ - E1102, calling a non callable object
+ - E1111 and W1111 when an assigment is done on a function call but the
+ infered function returns None (implements #10431)
+
+ * change in the base checker:
+
+ - checks module level and instance attribute names (new const-rgx
+ and attr-rgx configuration option) (implements #10209 and
+ #10440)
+ - list comprehension and generator expression variables have their
+ own regular expression (the inlinevar-rgx option) (implements
+ #9146)
+ - the C0101 check with its min-name-lentgh option has
+ been removed (this can be specified in the regxp after all...)
+ - W0103 and W0121 are now handled by the variables checker
+ (W0103 is now W0603 and W0604 has been splitted into different messages)
+ - W0131 and W0132 messages have been reclassified to C0111 and
+ C0112 respectivly
+ - new W0104 message on statement without effect
+
+ * regexp support for dummy-variables (dummy-variables-rgx option
+ replace dummy-variables) (implements #10027)
+ * better global statement handling, see W0602, W0603, W0604 messages
+ (implements #10344 and #10236)
+ * --debug-mode option, disabling all checkers without error message
+ and filtering others to only display error
+ * fixed some R0201 (method could be a function) false positive
+
+
+2006-01-10 -- 0.9.0
+ * a lot of updates to follow astng 0.14 API changes, so install
+ logilab-astng 0.14 or greater before using this version of pylint
+ * checker number 10 ! newstyle will search for problems regarding old
+ style / new style classes usage problems (rely on astng 0.14 new
+ style detection feature)
+ * new 'load-plugins' options to load additional pylint plugins (usable
+ from the command line or from a configuration file) (implements
+ #10031)
+ * check if a "pylintrc" file exists in the current working directory
+ before using the one specified in the PYLINTRC environment variable
+ or the default ~/.pylintrc or /etc/pylintrc
+ * fixed W0706 (Identifier used to raise an exception is assigned...)
+ false positive and reraising a catched exception instance
+ * fixed E0611 (No name get in module blabla) false positive when accessing
+ to a class'__dict__
+ * fixed some E0203 ("access to member before its definition") false
+ positive
+ * fixed E0214 ("metaclass method frist argument should be mcs) false
+ positive with staticmethod used on a metaclass
+ * fixed packaging which was missing the test/regrtest_data directory
+ * W0212 (method could be a function) has been reclassified in the
+ REFACTOR category as R0201, and is no more considerer when a method
+ overrides an abstract method from an ancestor class
+ * include module name in W0401 (wildcard import), as suggested by
+ Amaury
+ * when using the '--parseable', path are written relative to the
+ current working directory if in a sub-directory of it (#9789)
+ * 'pylint --version' shows logilab-astng and logilab-common versions
+ * fixed pylint.el to handle space in file names
+ * misc lint style fixes
+
+
+2005-11-07 -- 0.8.1
+ * fix "deprecated module" false positive when the code imports a
+ module whose name starts with a deprecated module's name (close
+ #10061)
+ * fix "module has no name __dict__" false positive (close #10039)
+ * fix "access to undefined variable __path__" false positive (close
+ #10065)
+ * fix "explicit return in __init__" false positive when return is
+ actually in an inner function (close #10075)
+
+2005-10-21 -- 0.8.0
+ * check names imported from a module exists in the module (E0611),
+ patch contributed by Amaury Forgeot d'Arc
+ * print a warning (W0212) for methods that could be a function
+ (implements #9100)
+ * new --defining-attr-methods option on classes checker
+ * new --acquired-members option on the classes checker, used when
+ --zope=yes to avoid false positive on acquired attributes (listed
+ using this new option) (close #8616)
+ * generate one E0602 for each use of an undefined variable
+ (previously, only one for the first use but not for the following)
+ (implements #1000)
+ * make profile option saveable
+ * fix Windows .bat file, patch contributed by Amaury Forgeot d'Arc
+ * fix one more false positive for E0601 (access before definition)
+ with for loop such as "for i in range(10): print i" (test
+ func_noerror_defined_and_used_on_same_line)
+ * fix false positive for E0201 (undefined member) when accessing to
+ __name__ on a class object
+ * fix astng checkers traversal order
+ * fix bug in format checker when parsing a file from a platform
+ using different new line characters (close #9239)
+ * fix encoding detection regexp
+ * fix --rcfile handling (support for --rcfile=file, close #9590)
+
+
+2005-05-27 -- 0.7.0
+ * WARNING: pylint is no longer a logilab subpackage. Users may have to
+ manually remove the old logilab/pylint directory.
+ * introduce a new --additional-builtins option to handle user defined
+ builtins
+ * --reports option has now -r as short alias, and -i for --include-ids
+ * fix a bug in the variables checker which may causing some false
+ positives when variables are defined and used within the same
+ statement (test func_noerror_defined_and_used_on_same_line)
+ * this time, real fix of the "disable-msg in the config file" problem,
+ test added to unittest_lint
+ * fix bug with --list-messages and python -OO
+ * fix possible false positive for W0201
+
+
+2005-04-14 -- 0.6.4
+ * allow to parse files without extension when a path is given on the
+ command line (test noext)
+ * don't fail if we are unable to read an inline option (e.g. inside a
+ module), just produce an information message (test func_i0010)
+ * new message E0103 for break or continue outside loop (close #8883,
+ test func_continue_not_in_loop)
+ * fix bug in the variables checker, causing non detection of some
+ actual name error (close #8884, test
+ func_nameerror_on_string_substitution)
+ * fix bug in the classes checker which was making pylint crash if
+ "object" is assigned in a class inheriting from it (test
+ func_noerror_object_as_class_attribute)
+ * fix problem with the similar checker when related options are
+ defined in a configuration file
+ * new --generate-man option to generate pylint's man page (require the
+ latest logilab.common (>= 0.9.3)
+ * packaged (generated...) man page
+
+2005-02-24 -- 0.6.3
+ * fix scope problem which may cause false positive and true negative
+ on E0602
+ * fix problem with some options such as disable-msg causing error when
+ they are coming from the configuration file
+
+2005-02-16 -- 0.6.2
+ * fix false positive on E0201 ("access to undefined member") with
+ metaclasses
+ * fix false positive on E0203 ("access to member before its
+ definition") when attributes are defined in a parent class
+ * fix false positive on W0706 ("identifier used to raise an exception
+ assigned to...")
+ * fix interpretation of "\t" as value for the indent-string
+ configuration variable
+ * fix --rcfile so that --rcfile=pylintrc (only --rcfile pylintrc was
+ working in earlier release)
+ * new raw checker example in the examples/ directory
+
+2005-02-04 -- 0.6.1
+ * new --rcfile option to specify the configuration file without the
+ PYLINTRC environment variable
+ * added an example module for a custom pylint checker (see the
+ example/ directory)
+ * some fixes to handle fixes in common 0.9.1 (should however still working
+ with common 0.9.0, even if upgrade is recommended)
+
+2005-01-20 -- 0.6.0
+ * refix pylint emacs mode
+ * no more traceback when just typing "pylint"
+ * fix a bug which may cause crashes on resolving parent classes
+ * fix problems with the format checker: don't chock on files
+ containing multiple CR, avoid C0322, C0323, C0324 false positives
+ with triple quoted string with quote inside
+ * correctly detect access to member defined latter in __init__ method
+ * now depends on common 0.8.1 to fix problem with interface resolution
+ (close #8606)
+ * new --list-msgs option describing available checkers and their
+ messages
+ * added windows specific documentation to the README file, contributed
+ by Brian van den Broek
+ * updated doc/features.txt (actually this file is now generated using
+ the --list-msgs option), more entries into the FAQ
+ * improved tests coverage
+
+
+2004-10-19 -- 0.5.0
+ * avoid to import analyzed modules !
+ * new Refactor and Convention message categories. Some Warnings have been
+ remaped into those new categories
+ * added "similar", a tool to find copied and pasted lines of code,
+ both using a specific command line tool and integrated as a
+ pylint's checker
+ * imports checker may report import dependancies as a dot graph
+ * new checker regrouping most Refactor detection (with some new metrics)
+ * more command line options storable in the configuration file
+ * fix bug with total / undocumented number of methods
+
+
+2004-07-08 -- 0.4.2
+ * fix pylint emacs mode
+ * fix classes checkers to handler twisted interfaces
+
+2004-05-14 -- 0.4.1
+ * fix the setup.py script to allow bdist_winst (well, the generated
+ installer has not been tested...) with the necessary
+ logilab/__init__.py file
+ * fix file naming convention as suggested by Andreas Amoroso
+ * fix stupid crash bug with bad method names
+
+2004-05-10 -- 0.4.0
+ * fix file path with --parsable
+ * --parsable option has been renamed to --parseable
+ * added patch from Andreas Amoroso to output message to files instead
+ of standard output
+ * added Run to the list of correct variable names
+ * fix variable names regexp and checking of local classes names
+ * some basic handling of metaclasses
+ * no-docstring-rgx apply now on classes too
+ * new option to specify a different regexp for methods than for
+ functions
+ * do not display the evaluation report when no statements has been
+ analysed
+ * fixed crash with a class nested in a method
+ * fixed format checker to deals with triple quoted string and
+ lines with code and comment mixed
+ * use logilab.common.ureports to layout reports
+
+
+2004-02-17 -- 0.3.3
+ * added a parsable text output, used when the --parsable option is
+ provided
+ * added an emacs mode using this output, availabe in the distrib's
+ elisp directory
+ * fixed some typos in messages
+ * change include-ids options to yn, and allow it to be in the
+ configuration file
+ * do not chock on corrupted stats files
+ * fixed bug in the format checker which may stop pylint execution
+ * provide scripts for unix and windows to wrap the minimal pylint tk
+ gui
+
+2003-12-23 -- 0.3.2
+ * html-escape messages in the HTML reporter (bug reported by Juergen
+ Hermann)
+ * added "TODO" to the list of default note tags
+ * added "rexec" to the list of default deprecated modules
+ * fixed typos in some messages
+
+2003-12-05 -- 0.3.1
+ * bug fix in format and classes checkers
+ * remove print statement from imports checkers
+ * provide a simple tk gui, essentially usefull for windows users
+
+2003-11-20 -- 0.3.0
+ * new exceptions checker, checking for string exception and empty
+ except clauses.
+ * imports checker checks for reimport of modules
+ * classes checker checks for calls to ancestor's __init__ and abstract
+ method not overriden. It doesn't complain anymore for unused import in
+ __init__ files, and provides a new option ignore-interface-methods,
+ usefull when you're using zope Interface implementation in your project
+ * base checker checks for black listed builtins call (controled by the
+ bad-functions option) and for use of * and **
+ * format checker checks for use of <> and "l" as long int marker
+ * major internal API changes
+ * use the rewrite of astng, based on compiler.ast
+ * added unique id for messages, as suggested by Wolfgang Grafen
+ * added unique id for reports
+ * can take multiple modules or files as argument
+ * new options command line options : --disable-msg, --enable-msg,
+ --help-msg, --include-ids, --reports, --disable-report, --cache-size
+ * --version shows the version of the python interpreter
+ * removed some options which are now replaced by [en|dis]able-msg, or
+ disable-report
+ * read disable-msg and enable-msg options in source files (should be
+ in comments on the top of the file, in the form
+ "# pylint: disable-msg=W0402"
+ * new message for modules importing themselves instead of the "cyclic
+ import" message
+ * fix bug with relative and cyclic imports
+ * fix bug in imports checker (cycle was not always detected)
+ * still fixes in format checker : don't check comment and docstring,
+ check first line after an indent
+ * black and white list now apply to all identifiers, not only
+ variables, so changed the configuration option from
+ (good|bad)-variable-names to (good|bad)-names
+ * added string, rexec and Bastion to the default list of deprecated
+ modules
+ * do not print redefinition warning for function/class/method defined
+ in mutually exclusive branchs
+
+
+2003-10-10 -- 0.2.1
+ * added some documentation, fixed some typos
+ * set environment variable PYLINT_IMPORT to 1 during pylint execution.
+ * check that variables "imported" using the global statement exist
+ * indentation problems are now warning instead of errors
+ * fix checkers.initialize to try to load all files with a known python
+ extension (patch from wrobell)
+ * fix a bunch of messages
+ * fix sample configuration file
+ * fix the bad-construction option
+ * fix encoding checker
+ * fix format checker
+
+2003-09-12 -- 0.2.0
+ * new source encoding / FIXME checker (pep 263)
+ * new --zope option which trigger Zope import. Usefull to check Zope
+ products code.
+ * new --comment option which enable the evaluation note comment
+ (disabled by default).
+ * a ton of bug fixes
+ * easy functionnal test infrastructure
+
+
+2003-06-18 -- 0.1.2
+ * bug fix release
+ * remove dependency to pyreverse
+
+2003-06-01 -- 0.1.1
+ * much more functionnalities !
+
+2003-05-19 -- 0.1
+ * initial release
diff --git a/DEPENDS b/DEPENDS
new file mode 100644
index 0000000..acbe171
--- /dev/null
+++ b/DEPENDS
@@ -0,0 +1,3 @@
+python-logilab-common (>= 0.19.0)
+python-logilab-astng (>= 0.16.1)
+python-tk
diff --git a/MANIFEST.in b/MANIFEST.in
new file mode 100644
index 0000000..dde44eb
--- /dev/null
+++ b/MANIFEST.in
@@ -0,0 +1,21 @@
+include COPYING
+include DEPENDS
+include ChangeLog
+include TODO
+include bin/symilar
+include bin/pylint
+include bin/*.bat
+include bin/pylint-gui
+include bin/epylint
+include examples/pylintrc*
+include examples/*.py
+include elisp/*.el
+include elisp/startup
+include man/pylint.1
+recursive-include doc *.txt *.html
+recursive-include test/input *.py similar* noext
+recursive-include test/messages *.txt *.txt2
+recursive-include test/rpythoninput *.py
+recursive-include test/rpythonmessages *.txt
+recursive-include test/regrtest_data *.py
+include test/fulltest.sh
diff --git a/README b/README
new file mode 100644
index 0000000..fc5aaba
--- /dev/null
+++ b/README
@@ -0,0 +1,59 @@
+README for PyLint
+=================
+
+Dependencies
+------------
+Pylint requires the logilab-astng (version >= 0.14), logilab-common
+(version >= 0.13) and the optik (only for python < 2.3) packages.
+Pylint should be compatible with any python >= 2.2.
+
+* http://www.logilab.org/projects/astng
+* http://www.logilab.org/projects/common
+* http://optik.sourceforge.net/
+
+
+Install
+-------
+From the source distribution, extract the tarball and run ::
+
+ python setup.py install
+
+You'll have to install dependancies in a similar way. For debian and
+rpm packages, use your usual tools according to your Linux distribution.
+
+More information about installation and available distribution format
+may be found in the user manual in the *doc* subdirectory.
+
+
+Documentation
+-------------
+Look in the doc/ subdirectory.
+
+
+Comments, support, bug reports
+------------------------------
+Use the python-projects@logilab.org mailing list. Since we do not have
+publicly available bug tracker yet, bug reports should be emailed
+there too.
+
+You can subscribe to this mailing list at
+http://www.logilab.org/mailinglists/python_projects/mailinglist_register_form
+
+Archives are available at
+http://lists.logilab.org/pipermail/python-projects/
+
+
+Contributors
+------------
+* Sylvain Thénault: main author / maintainer
+* Alexandre Fayolle: TkInter gui, documentation, debian support
+* Brian van den Broek: windows installation documentation
+* Amaury Forgeot d'Arc: patch to check names imported from a module
+ exists in the module
+* Benjamin Niemann: patch to allow block level enabling/disabling of messages
+* Wolfgang Grafen, Axel Muller, Fabio Zadrozny, Pierre Rouleau,
+ Maarten ter Huurne, Mirko Friedenhagen (among others):
+ bug reports, feedback, feature requests...
+* All the Logilab's team: daily use, bug reports, feature requests
+* Other people have contributed by their feedback, if I've forgotten
+ you, send me a note !
diff --git a/TODO b/TODO
new file mode 100644
index 0000000..aaf8472
--- /dev/null
+++ b/TODO
@@ -0,0 +1,46 @@
+PyLint's TODO list
+------------------
+
+
+- un rapport avec les métriques vues dans TDD
+- métrique manquantes
+- tests
+
+* faire tourner sur wxpython...
+
+* test external dependancies
+
+
+* avoir les options liés à un message id dans son aide
+
+* avoir les messages id géré par un checker dans --help
+
+* avoir la valeur courante des options dans --help
+
+* doc développeur
+
+* supporter des wildcards dans disable-msg ?
+
+* voir notes gvr sur main
+
+* récupérer les phrases d'évaluation dans un fichier texte
+
+* i18n avec gettext
+
+* commenter les regexp de format.py
+
+* gestion nested_scopes (modes py2.1, 2.2... ?)
+
+* checkers :
+ - vérifier arguments __new__
+ - compléter format checker
+ voir http://www.python.org/peps/pep-0008.html
+ - vérifier classes sans __init__ mais avec plusieurs ancêtres ayant
+ un __init__
+ - opérateur % avec des formats ne correspondant pas aux arguments
+ - mauvais nombre d'arguments passés à une méthode ou fonction
+ - utilisation constante dans condition
+ - gestion del statements
+ - vérification utilisation __getattribute__, __slots__ dans new
+ style class seulement
+ - vérification assignements quand __slots__
diff --git a/__init__.py b/__init__.py
new file mode 100644
index 0000000..0c4bd13
--- /dev/null
+++ b/__init__.py
@@ -0,0 +1,16 @@
+# 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.,
+# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+""" Copyright (c) 2002-2008 LOGILAB S.A. (Paris, FRANCE).
+http://www.logilab.fr/ -- mailto:contact@logilab.fr
+"""
+
diff --git a/__pkginfo__.py b/__pkginfo__.py
new file mode 100644
index 0000000..4330f3d
--- /dev/null
+++ b/__pkginfo__.py
@@ -0,0 +1,73 @@
+# pylint: disable-msg=W0622,C0103
+# Copyright (c) 2003-2008 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.,
+# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+"""pylint packaging information"""
+
+modname = 'pylint'
+
+numversion = (0, 14, 0)
+version = '.'.join([str(num) for num in numversion])
+
+license = 'GPL'
+copyright = '''Copyright (c) 2003-2008 Sylvain Thenault (thenault@gmail.com).
+Copyright (c) 2003-2008 LOGILAB S.A. (Paris, FRANCE).
+http://www.logilab.fr/ -- mailto:contact@logilab.fr'''
+
+short_desc = "python code static checker"
+long_desc = """\
+ Pylint is a Python source code analyzer which looks for programming
+ errors, helps enforcing a coding standard and sniffs for some code
+ smells (as defined in Martin Fowler's Refactoring book)
+ .
+ Pylint can be seen as another PyChecker since nearly all tests you
+ can do with PyChecker can also be done with Pylint. However, Pylint
+ offers some more features, like checking length of lines of code,
+ checking if variable names are well-formed according to your coding
+ standard, or checking if declared interfaces are truly implemented,
+ and much more.
+ .
+ Additionally, it is possible to write plugins to add your own checks."""
+
+author = "Sylvain Thenault"
+author_email = "sylvain.thenault@logilab.fr"
+
+web = "http://www.logilab.org/project/name/%s" % modname
+ftp = "ftp://ftp.logilab.org/pub/%s" % modname
+mailinglist = "mailto://python-projects@logilab.org"
+
+from os.path import join
+scripts = [join('bin', filename)
+ for filename in ('pylint', 'pylint-gui', "symilar", "epylint")]
+
+include_dirs = [join('test', 'input'),
+ join('test', 'messages'),
+ join('test', 'rpythonmessages'),
+ join('test', 'regrtest_data')]
+
+pyversions = ["2.3", "2.4", "2.5"]
+
+debian_uploader = 'Alexandre Fayolle <afayolle@debian.org>'
+
+classifiers = ['Development Status :: 4 - Beta',
+ 'Environment :: Console',
+ 'Intended Audience :: Developers',
+ 'License :: OSI Approved :: GNU General Public License (GPL)',
+ 'Operating System :: OS Independent',
+ 'Programming Language :: Python',
+ 'Topic :: Software Development :: Debuggers',
+ 'Topic :: Software Development :: Quality Assurance',
+ 'Topic :: Software Development :: Testing',
+ ]
diff --git a/announce.txt b/announce.txt
new file mode 100644
index 0000000..769f8a3
--- /dev/null
+++ b/announce.txt
@@ -0,0 +1,42 @@
+What's new ?
+------------
+%(CHANGELOG)s
+
+
+What is pylint ?
+----------------
+
+Pylint is a python tool that checks if a module satisfy a coding
+standard. Pylint can be seen as another pychecker since nearly all
+tests you can do with pychecker can also be done with Pylint. But
+Pylint offers some more features, like checking line-code's length,
+checking if variable names are well-formed according to your coding
+standard, or checking if declared interfaces are truly implemented,
+and much more (see http://www.logilab.org/projects/pylint/ for the
+complete check list). The big advantage with Pylint is that it is
+highly configurable, customizable, and you can easily write a small
+plugin to add a personal feature.
+
+The usage it quite simple :
+
+$ pylint mypackage.mymodule
+
+
+This command will output all the errors and warnings related to the
+tested code (here : mypackage.mymodule), will dump a little summary at
+the end, and will give a mark to the tested code.
+
+Pylint is free software distributed under the GNU Public Licence.
+
+
+Home page
+---------
+%(WEB)s
+
+Download
+--------
+%(FTP)s
+
+Mailing list
+------------
+%(MAILINGLIST)s
diff --git a/bin/epylint b/bin/epylint
new file mode 100755
index 0000000..b437e8a
--- /dev/null
+++ b/bin/epylint
@@ -0,0 +1,29 @@
+#!/usr/bin/env python
+
+import re
+import sys
+
+from subprocess import *
+
+p = Popen("pylint -f parseable -r n --disable-msg-cat=C,R %s" %
+ sys.argv[1], shell = True, stdout = PIPE).stdout
+
+for line in p:
+ match = re.search("\\[([WE])(, (.+?))?\\]", line)
+ if match:
+ kind = match.group(1)
+ func = match.group(3)
+
+ if kind == "W":
+ msg = "Warning"
+ else:
+ msg = "Error"
+
+ if func:
+ line = re.sub("\\[([WE])(, (.+?))?\\]",
+ "%s (%s):" % (msg, func), line)
+ else:
+ line = re.sub("\\[([WE])?\\]", "%s:" % msg, line)
+ print line,
+
+p.close()
diff --git a/bin/pylint b/bin/pylint
new file mode 100755
index 0000000..e20e031
--- /dev/null
+++ b/bin/pylint
@@ -0,0 +1,4 @@
+#!/usr/bin/env python
+import sys
+from pylint import lint
+lint.Run(sys.argv[1:])
diff --git a/bin/pylint-gui b/bin/pylint-gui
new file mode 100755
index 0000000..025378f
--- /dev/null
+++ b/bin/pylint-gui
@@ -0,0 +1,7 @@
+#!/usr/bin/env python
+import sys
+try:
+ from pylint import gui
+ gui.Run(sys.argv[1:])
+except ImportError:
+ sys.exit('tkinter is not available')
diff --git a/bin/pylint-gui.bat b/bin/pylint-gui.bat
new file mode 100644
index 0000000..68d552e
--- /dev/null
+++ b/bin/pylint-gui.bat
@@ -0,0 +1,20 @@
+@echo off
+rem = """-*-Python-*- script
+@echo off
+rem -------------------- DOS section --------------------
+rem You could set PYTHONPATH or TK environment variables here
+python -x %~f0 %*
+goto exit
+
+"""
+# -------------------- Python section --------------------
+import sys
+from pylint import gui
+gui.Run(sys.argv[1:])
+
+
+DosExitLabel = """
+:exit
+rem """
+
+
diff --git a/bin/pylint.bat b/bin/pylint.bat
new file mode 100644
index 0000000..772735a
--- /dev/null
+++ b/bin/pylint.bat
@@ -0,0 +1,19 @@
+@echo off
+rem = """-*-Python-*- script
+rem -------------------- DOS section --------------------
+rem You could set PYTHONPATH or TK environment variables here
+python -x %~f0 %*
+goto exit
+
+"""
+# -------------------- Python section --------------------
+import sys
+from pylint import lint
+lint.Run(sys.argv[1:])
+
+
+DosExitLabel = """
+:exit
+rem """
+
+
diff --git a/bin/symilar b/bin/symilar
new file mode 100755
index 0000000..7ca139f
--- /dev/null
+++ b/bin/symilar
@@ -0,0 +1,3 @@
+#!/usr/bin/env python
+from pylint.checkers import similar
+similar.run()
diff --git a/bin/symilar.bat b/bin/symilar.bat
new file mode 100644
index 0000000..5c9bd0e
--- /dev/null
+++ b/bin/symilar.bat
@@ -0,0 +1,20 @@
+@echo off
+rem = """-*-Python-*- script
+@echo off
+rem -------------------- DOS section --------------------
+rem You could set PYTHONPATH or TK environment variables here
+python -x %~f0 %*
+goto exit
+
+"""
+# -------------------- Python section --------------------
+import sys
+from pylint.checkers import similar
+similar.run()
+
+
+DosExitLabel = """
+:exit
+rem """
+
+
diff --git a/checkers/__init__.py b/checkers/__init__.py
new file mode 100644
index 0000000..57c1ba1
--- /dev/null
+++ b/checkers/__init__.py
@@ -0,0 +1,164 @@
+# Copyright (c) 2003-2006 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.,
+# 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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: rpython
+
+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 tokenize
+from os import listdir
+from os.path import dirname, join, isdir, splitext
+
+from logilab.astng.utils import ASTWalker
+from logilab.common.configuration import OptionsProviderMixIn
+
+from pylint.reporters import diff_string, EmptyReport
+
+def table_lines_from_stats(stats, old_stats, columns):
+ """get values listed in <columns> from <stats> and <old_stats>,
+ 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
+ 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, ASTWalker):
+ """base class for checkers"""
+
+ options = ()
+ priority = -9
+ enabled = True
+ may_be_disabled = True
+ name = None
+
+ def __init__(self, linter=None):
+ """checker instances should have the linter as argument
+
+ linter is an object implementing ILinter
+ """
+ ASTWalker.__init__(self, self)
+ self.name = self.name.lower()
+ OptionsProviderMixIn.__init__(self)
+ self.linter = linter
+
+ def add_message(self, msg_id, line=None, node=None, args=None):
+ """add a message of a given type"""
+ self.linter.add_message(msg_id, line, node, args)
+
+ def is_enabled(self):
+ """return true if the checker is enabled"""
+ return self.enabled
+
+ def enable(self, enabled):
+ """enable / disable this checker if true / false is given
+
+ it false values has no effect if the checker can't be disabled
+ """
+ if not enabled and not self.may_be_disabled:
+ raise Exception("can't disable %s checker" % self.name)
+ if enabled or self.may_be_disabled:
+ self.enabled = enabled
+
+ def package_dir(self):
+ """return the base directory for the analysed package"""
+ return dirname(self.linter.base_file)
+
+
+ # 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 BaseRawChecker(BaseChecker):
+ """base class for raw checkers"""
+
+ def process_module(self, stream):
+ """process a module
+
+ the module's content is accessible via the stream object
+
+ stream must implements the readline method
+ """
+ self.process_tokens(tokenize.generate_tokens(stream.readline))
+
+ def process_tokens(self, tokens):
+ """should be overiden by subclasses"""
+ raise NotImplementedError()
+
+
+PY_EXTS = ('.py', '.pyc', '.pyo', '.pyw', '.so', '.dll')
+
+def initialize(linter):
+ """initialize linter with checkers in this package """
+ package_load(linter, __path__[0])
+
+def package_load(linter, directory):
+ """load all module and package in the given directory, looking for a
+ 'register' function in each one, used to register pylint checkers
+ """
+ globs = globals()
+ imported = {}
+ for filename in listdir(directory):
+ basename, extension = splitext(filename)
+ if not imported.has_key(basename) and (
+ (extension in PY_EXTS and basename != '__init__') or (
+ not extension and not basename == 'CVS' and
+ isdir(join(directory, basename)))):
+ try:
+ module = __import__(basename, globs, globs, None)
+ except ValueError:
+ # empty module name (usually emacs auto-save files)
+ continue
+ except ImportError:
+ import sys
+ print >> sys.stderr, "Problem importing module: %s" % filename
+ else:
+ if hasattr(module, 'register'):
+ module.register(linter)
+ imported[basename] = 1
+
+__all__ = ('CheckerHandler', 'BaseChecker', 'initialize', 'package_load')
diff --git a/checkers/base.py b/checkers/base.py
new file mode 100644
index 0000000..d2fc9f6
--- /dev/null
+++ b/checkers/base.py
@@ -0,0 +1,556 @@
+# Copyright (c) 2003-2007 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.,
+# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+"""basic checker for Python code
+"""
+
+from logilab import astng
+from logilab.common.ureports import Table
+
+from pylint.interfaces import IASTNGChecker
+from pylint.reporters import diff_string
+from pylint.checkers import BaseChecker
+from pylint.checkers.utils import are_exclusive
+
+import re
+
+# regex for class/function/variable/constant nane
+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}$')
+# do not require a doc string on system methods
+NO_REQUIRED_DOC_RGX = re.compile('__.*__')
+
+del re
+
+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, (astng.For, astng.ListComp, astng.GenExpr)):
+ return True
+ parent = parent.parent
+ return False
+
+def in_nested_list(nested_list, obj):
+ """return true if the object is an element of <nested_list> 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 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'):
+ nice_stats[node_type] = {}
+ total = stats[node_type]
+ if total == 0:
+ doc_percent = 0
+ badname_percent = 0
+ else:
+ documented = total - stats['undocumented_'+node_type]
+ doc_percent = float((documented)*100) / total
+ badname_percent = (float((stats['badname_'+node_type])*100)
+ / total)
+ nice_stats[node_type]['percent_documented'] = doc_percent
+ nice_stats[node_type]['percent_badname'] = badname_percent
+ 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,
+ '%.2f' % nice_stats[node_type]['percent_documented'],
+ '%.2f' % nice_stats[node_type]['percent_badname'])
+ sect.append(Table(children=lines, cols=6, rheaders=1))
+
+
+MSGS = {
+ 'E0100': ('__init__ method is a generator',
+ 'Used when the special class method __init__ is turned into a '
+ 'generator by a yield in its body.'),
+ 'E0101': ('Explicit return in __init__',
+ 'Used when the special class method __init__ has an explicit \
+ return value.'),
+ 'E0102': ('%s already defined line %s',
+ 'Used when a function / class / method is redefined.'),
+ 'E0103': ('%r not properly in loop',
+ 'Used when break or continue keywords are used outside a loop.'),
+
+ 'E0104': ('Return outside function',
+ 'Used when a "return" statement is found outside a function or '
+ 'method.'),
+ 'E0105': ('Yield outside function',
+ 'Used when a "yield" statement is found outside a function or '
+ 'method.'),
+ 'E0106': ('Return with argument inside generator',
+ 'Used when a "return" statement with an argument is found '
+ 'outside in a generator function or method (e.g. with some '
+ '"yield" statements).'),
+
+ 'W0101': ('Unreachable code',
+ 'Used when there is some code behind a "return" or "raise" \
+ statement, which will never be accessed.'),
+ 'W0102': ('Dangerous default value %s as argument',
+ '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',
+ 'Used when a statement doesn\'t have (or at least seems to) \
+ any effect.'),
+ 'W0105': ('String statement has no effect',
+ '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': ('Unnecessary semicolon',
+ 'Used when a statement is endend by a semi-colon (";"), which \
+ isn\'t necessary (that\'s python, not C ;).'),
+ 'W0107': ('Unnecessary pass statement',
+ 'Used when a "pass" statement that can be avoided is '
+ 'encountered.)'),
+
+ 'W0122': ('Use of the exec statement',
+ 'Used when you use the "exec" statement, to discourage its \
+ usage. That doesn\'t mean you can not use it !'),
+
+ 'W0141': ('Used builtin function %r',
+ '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',
+ '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.'),
+
+ 'C0102': ('Black listed name "%s"',
+ 'Used when the name is listed in the black list (unauthorized \
+ names).'),
+ 'C0103': ('Invalid name "%s" (should match %s)',
+ 'Used when the name doesn\'t match the regular expression \
+ associated to its type (constant, variable, class...).'),
+
+ 'C0111': ('Missing docstring', # W0131
+ 'Used when a module, function, class or method has no docstring.\
+ Some special methods like __init__ doesn\'t necessary require a \
+ docstring.'),
+ 'C0112': ('Empty docstring', # W0132
+ 'Used when a module, function, class or method has an empty \
+ docstring (it would be too easy ;).'),
+
+ 'C0121': ('Missing required attribute "%s"', # W0103
+ 'Used when an attribute required for modules is missing.'),
+
+ }
+
+class BasicChecker(BaseChecker):
+ """checks for :
+ * doc strings
+ * modules / classes / functions / methods / arguments / variables name
+ * number of arguments, local variables, branchs, 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__ = IASTNGChecker
+
+ name = 'basic'
+ msgs = MSGS
+ priority = -1
+ options = (('required-attributes',
+ {'default' : (), 'type' : 'csv',
+ 'metavar' : '<attributes>',
+ 'help' : 'Required attributes for module, separated by a '
+ 'comma'}
+ ),
+ ('no-docstring-rgx',
+ {'default' : NO_REQUIRED_DOC_RGX,
+ 'type' : 'regexp', 'metavar' : '<regexp>',
+ 'help' : 'Regular expression which should only match '
+ 'functions or classes name which do not require a '
+ 'docstring'}
+ ),
+## ('min-name-length',
+## {'default' : 3, 'type' : 'int', 'metavar' : '<int>',
+## 'help': 'Minimal length for module / class / function / '
+## 'method / argument / variable names'}
+## ),
+ ('module-rgx',
+ {'default' : MOD_NAME_RGX,
+ 'type' :'regexp', 'metavar' : '<regexp>',
+ 'help' : 'Regular expression which should only match correct '
+ 'module names'}
+ ),
+ ('const-rgx',
+ {'default' : CONST_NAME_RGX,
+ 'type' :'regexp', 'metavar' : '<regexp>',
+ 'help' : 'Regular expression which should only match correct '
+ 'module level names'}
+ ),
+ ('class-rgx',
+ {'default' : CLASS_NAME_RGX,
+ 'type' :'regexp', 'metavar' : '<regexp>',
+ 'help' : 'Regular expression which should only match correct '
+ 'class names'}
+ ),
+ ('function-rgx',
+ {'default' : DEFAULT_NAME_RGX,
+ 'type' :'regexp', 'metavar' : '<regexp>',
+ 'help' : 'Regular expression which should only match correct '
+ 'function names'}
+ ),
+ ('method-rgx',
+ {'default' : DEFAULT_NAME_RGX,
+ 'type' :'regexp', 'metavar' : '<regexp>',
+ 'help' : 'Regular expression which should only match correct '
+ 'method names'}
+ ),
+ ('attr-rgx',
+ {'default' : DEFAULT_NAME_RGX,
+ 'type' :'regexp', 'metavar' : '<regexp>',
+ 'help' : 'Regular expression which should only match correct '
+ 'instance attribute names'}
+ ),
+ ('argument-rgx',
+ {'default' : DEFAULT_NAME_RGX,
+ 'type' :'regexp', 'metavar' : '<regexp>',
+ 'help' : 'Regular expression which should only match correct '
+ 'argument names'}),
+ ('variable-rgx',
+ {'default' : DEFAULT_NAME_RGX,
+ 'type' :'regexp', 'metavar' : '<regexp>',
+ 'help' : 'Regular expression which should only match correct '
+ 'variable names'}
+ ),
+ ('inlinevar-rgx',
+ {'default' : COMP_VAR_RGX,
+ 'type' :'regexp', 'metavar' : '<regexp>',
+ 'help' : 'Regular expression which should only match correct '
+ 'list comprehension / generator expression variable \
+ names'}
+ ),
+ ('good-names',
+ {'default' : ('i', 'j', 'k', 'ex', 'Run', '_'),
+ 'type' :'csv', 'metavar' : '<names>',
+ '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' : '<names>',
+ 'help' : 'Bad variable names which should always be refused, '
+ 'separated by a comma'}
+ ),
+
+ ('bad-functions',
+ {'default' : ('map', 'filter', 'apply', 'input'),
+ 'type' :'csv', 'metavar' : '<builtin function names>',
+ 'help' : 'List of builtins function names that should not be '
+ 'used, separated by a comma'}
+ ),
+ )
+ reports = ( ('R0101', 'Statistics by type', report_by_type_stats), )
+
+ def __init__(self, linter):
+ BaseChecker.__init__(self, linter)
+ self.stats = None
+ self._returns = None
+
+ def open(self):
+ """initialize visit variables and statistics
+ """
+ self._returns = []
+ self.stats = self.linter.add_stats(module=0, function=0,
+ method=0, class_=0,
+ 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,
+ undocumented_module=0,
+ undocumented_function=0,
+ undocumented_method=0,
+ undocumented_class=0)
+
+ def visit_module(self, node):
+ """check module name, docstring and required arguments
+ """
+ self.stats['module'] += 1
+ self._check_name('module', node.name.split('.')[-1], node)
+ self._check_docstring('module', node)
+ self._check_required_attributes(node, self.config.required_attributes)
+
+ def visit_class(self, node):
+ """check module name, docstring and redefinition
+ increment branch counter
+ """
+ self.stats['class'] += 1
+ self._check_name('class', node.name, node)
+ if self.config.no_docstring_rgx.match(node.name) is None:
+ self._check_docstring('class', node)
+ self._check_redefinition('class', node)
+ for attr, anodes in node.instance_attrs.items():
+ self._check_name('attr', attr, anodes[0])
+
+ def visit_discard(self, node):
+ """check for various kind of statements without effect"""
+ expr = node.expr
+ if isinstance(node.expr, astng.Const):
+ # XXX lineno maybe dynamically set incidently
+ if expr.value is None and expr.lineno is None:
+ # const None node with lineno to None are inserted
+ # on unnecessary semi-column
+ # XXX navigate to get a correct lineno
+ brothers = list(node.parent.getChildNodes())
+ previoussibling = brothers[brothers.index(node)-1]
+ self.add_message('W0106', node=previoussibling)
+ return
+ if isinstance(expr.value, basestring):
+ # tread string statement in a separated message
+ self.add_message('W0105', node=node)
+ return
+ # ignore if this is a function call (can't predicate side effects)
+ # or a yield (which are wrapped by a discard node in py >= 2.5)
+ if not isinstance(node.expr, (astng.CallFunc, astng.Yield)):
+ self.add_message('W0104', node=node)
+
+ def visit_pass(self, node):
+ """check is the pass statement is really necessary
+ """
+ # if self._returns is empty, we're outside a function !
+ if len(node.parent.getChildNodes()) > 1:
+ self.add_message('W0107', node=node)
+
+ def visit_function(self, node):
+ """check function name, docstring, arguments, redefinition,
+ variable names, max locals
+ """
+ is_method = node.is_method()
+ self._returns.append([])
+ f_type = is_method and 'method' or 'function'
+ self.stats[f_type] += 1
+ # function name
+ self._check_name(f_type, node.name, node)
+ # docstring
+ if self.config.no_docstring_rgx.match(node.name) is None:
+ self._check_docstring(f_type, node)
+ # check default arguments'value
+ self._check_defaults(node)
+ # check arguments name
+ args = node.argnames
+ if args is not None:
+ self._recursive_check_names(args, node)
+ # check for redefinition
+ self._check_redefinition(is_method and 'method' or 'function', node)
+
+ 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 node.is_method() and node.name == '__init__':
+ if node.is_generator():
+ self.add_message('E0100', node=node)
+ else:
+ values = [r.value for r in returns]
+ if [v for v in values if not (
+ (isinstance(v, astng.Const) and v.value is None)
+ or (isinstance(v, astng.Name) and v.name == 'None'))]:
+ self.add_message('E0101', node=node)
+ elif node.is_generator():
+ # make sure we don't mix non-None returns and yields
+ for retnode in returns:
+ if isinstance(retnode, astng.Return) and \
+ isinstance(retnode.value, astng.Const) and \
+ retnode.value.value is not None:
+ self.add_message('E0106', node=node,
+ line=retnode.fromlineno)
+
+ def visit_assname(self, node):
+ """check module level assigned names"""
+ frame = node.frame()
+ ass_type = node.ass_type()
+ if isinstance(ass_type, (astng.ListCompFor, astng.GenExprFor)):
+ self._check_name('inlinevar', node.name, node)
+ elif isinstance(frame, astng.Module):
+ if isinstance(ass_type, astng.Assign) and not in_loop(ass_type):
+ self._check_name('const', node.name, node)
+ elif isinstance(frame, astng.Function):
+ # global introduced variable aren't in the function locals
+ if node.name in frame:
+ self._check_name('variable', node.name, node)
+
+ def visit_return(self, node):
+ """check is the node has a right sibling (if so, that's some unreachable
+ code)
+ """
+ # if self._returns is empty, we're outside a function !
+ if not self._returns:
+ self.add_message('E0104', node=node)
+ return
+ self._returns[-1].append(node)
+ self._check_unreachable(node)
+
+ def visit_yield(self, node):
+ """check is the node has a right sibling (if so, that's some unreachable
+ code)
+ """
+ # if self._returns is empty, we're outside a function !
+ if not self._returns:
+ self.add_message('E0105', node=node)
+ return
+ self._returns[-1].append(node)
+
+ def visit_continue(self, node):
+ """check is the node has a right sibling (if so, that's some unreachable
+ code)
+ """
+ self._check_unreachable(node)
+ self._check_in_loop(node, 'continue')
+
+ def visit_break(self, node):
+ """check is the node has a right sibling (if so, that's some unreachable
+ code)
+ """
+ self._check_unreachable(node)
+ self._check_in_loop(node, 'break')
+
+ def visit_raise(self, node):
+ """check is the node has a right sibling (if so, that's some unreachable
+ code)
+ """
+ self._check_unreachable(node)
+
+ def visit_exec(self, node):
+ """just pring a warning on exec statements"""
+ self.add_message('W0122', node=node)
+
+ 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.node, astng.Name):
+ name = node.node.name
+ # ignore the name if it's not a builtin (ie not defined in the
+ # locals nor globals scope)
+ if not (node.frame().has_key(name) or
+ node.root().has_key(name)):
+ if name in self.config.bad_functions:
+ self.add_message('W0141', node=node, args=name)
+ if node.star_args or node.dstar_args:
+ self.add_message('W0142', node=node.node)
+
+
+ def _check_unreachable(self, node):
+ """check unreachable code"""
+ unreach_stmt = node.next_sibling()
+ if unreach_stmt is not None:
+ self.add_message('W0101', node=unreach_stmt)
+
+ 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, (astng.For, astng.While)):
+ break
+ _node = _node.parent
+ else:
+ self.add_message('E0103', node=node, args=node_name)
+
+ def _check_redefinition(self, redef_type, 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('E0102', node=node,
+ args=(redef_type, defined_self.lineno))
+
+ def _check_docstring(self, node_type, node):
+ """check the node has a non empty docstring"""
+ docstring = node.doc
+ if docstring is None:
+ self.stats['undocumented_'+node_type] += 1
+ self.add_message('C0111', node=node)
+ elif not docstring.strip():
+ self.stats['undocumented_'+node_type] += 1
+ self.add_message('C0112', node=node)
+
+ def _recursive_check_names(self, args, node):
+ """check names in a possibly recursive list <arg>"""
+ for arg in args:
+ if type(arg) is type(''):
+ self._check_name('argument', arg, node)
+ else:
+ self._recursive_check_names(arg, node)
+
+ def _check_name(self, node_type, name, node):
+ """check for a name using the type's regexp"""
+ if name in self.config.good_names:
+ return
+ if name in self.config.bad_names:
+ self.stats['badname_' + node_type] += 1
+ self.add_message('C0102', node=node, args=name)
+ return
+ regexp = getattr(self.config, node_type + '_rgx')
+ if regexp.match(name) is None:
+ self.add_message('C0103', node=node, args=(name, regexp.pattern))
+ self.stats['badname_' + node_type] += 1
+
+
+ def _check_defaults(self, node):
+ """check for dangerous default values as arguments"""
+ for default in node.defaults:
+ try:
+ value = default.infer().next()
+ except astng.InferenceError:
+ continue
+ if isinstance(value, (astng.Dict, astng.List)):
+ if value is default:
+ msg = default.as_string()
+ else:
+ msg = '%s (%s)' % (default.as_string(), value.as_string())
+ self.add_message('W0102', node=node, args=(msg,))
+
+ def _check_required_attributes(self, node, attributes):
+ """check for required attributes"""
+ for attr in attributes:
+ if not node.has_key(attr):
+ self.add_message('C0121', node=node, args=attr)
+
+
+def register(linter):
+ """required method to auto register this checker"""
+ linter.register_checker(BasicChecker(linter))
+
diff --git a/checkers/classes.py b/checkers/classes.py
new file mode 100644
index 0000000..a32c89c
--- /dev/null
+++ b/checkers/classes.py
@@ -0,0 +1,512 @@
+# Copyright (c) 2003-2007 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.,
+# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+"""classes checker for Python code
+"""
+from __future__ import generators
+
+from logilab.common.compat import set
+from logilab import astng
+
+from pylint.interfaces import IASTNGChecker
+from pylint.checkers import BaseChecker
+from pylint.checkers.utils import overrides_a_method
+
+MSGS = {
+ 'F0202': ('Unable to check methods signature (%s / %s)',
+ 'Used when PyLint has been unable to check methods signature \
+ compatibility for an unexpected raison. Please report this kind \
+ if you don\'t make sense of it.'),
+
+ 'E0202': ('An attribute inherited from %s hide this method',
+ 'Used when a class defines a method which is hiden by an \
+ instance attribute from an ancestor class.'),
+ 'E0203': ('Access to member %r before its definition line %s',
+ 'Used when an instance member is accessed before it\'s actually\
+ assigned.'),
+ 'W0201': ('Attribute %r 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
+ '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',
+ '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',
+ 'Used when a method has an attribute different the "self" as\
+ first argument. This is considered as an error since this is\
+ a soooo common convention that you should\'nt break it!'),
+ 'C0202': ('Class method should have "cls" as first argument', # E0212
+ 'Used when a class method has an attribute different than "cls"\
+ as first argument, to easily differentiate them from regular \
+ instance methods.'),
+ 'C0203': ('Metaclass method should have "mcs" as first argument', # E0214
+ 'Used when a metaclass method has an attribute different the \
+ "mcs" as first argument.'),
+
+ 'W0211': ('Static method with %r as first argument',
+ 'Used when a static method has "self" or "cls" as first argument.'
+ ),
+ 'R0201': ('Method could be a function',
+ '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',
+ 'Used when a class claims to implement an interface which is not \
+ a class.'),
+ 'E0222': ('Missing method %r from %s interface' ,
+ 'Used when a method declared in an interface is missing from a \
+ class implementing this interface'),
+ 'W0221': ('Arguments number differs from %s method',
+ '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 method',
+ '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',
+ 'Used when an abstract method (ie raise NotImplementedError) is \
+ not overridden in concrete class.'
+ ),
+ 'F0220': ('failed to resolve interfaces implemented by %s (%s)', # W0224
+ 'Used when a PyLint as failed to find interfaces implemented by \
+ a class'),
+
+
+ 'W0231': ('__init__ method from base class %r is 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',
+ 'Used when a class has no __init__ method, neither its parent \
+ classes.'),
+ 'W0233': ('__init__ method from a non direct base class %r is called',
+ 'Used when an __init__ method is called on a class which is not \
+ in the direct ancestors for the analysed class.'),
+
+ }
+
+
+class ClassChecker(BaseChecker):
+ """checks for :
+ * methods without self as first argument
+ * overridden methods signature
+ * access only to existant members via self
+ * attributes not defined in the __init__ method
+ * supported interfaces implementation
+ * unreachable code
+ """
+
+ __implements__ = (IASTNGChecker,)
+
+ # 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' : '<method names>',
+ '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' : '<method names>',
+ 'help' : 'List of method names used to declare (i.e. assign) \
+instance attributes.'}
+ ),
+
+ )
+
+ 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({})
+ 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 astng.NotFoundError:
+ self.add_message('W0232', args=node, node=node)
+
+ def leave_class(self, cnode):
+ """close a class node:
+ check that instance attributes are defined in __init__ and check
+ access to existant members
+ """
+ # checks attributes are defined in an allowed method such as __init__
+ defining_methods = self.config.defining_attr_methods
+ for attr, nodes in cnode.instance_attrs.items():
+ node = nodes[0] # XXX
+ frame = node.frame()
+ if frame.name not in defining_methods:
+ # check attribute is defined in a parent's __init__
+ for parent in cnode.instance_attr_ancestors(attr):
+ frame = parent.instance_attrs[attr][0].frame() # XXX
+ if frame.name in defining_methods:
+ # we're done :)
+ break
+ else:
+ # check attribute is defined as a class attribute
+ try:
+ cnode.local_attr(attr)
+ except astng.NotFoundError:
+ self.add_message('W0201', args=attr, node=node)
+ # check access to existant members on non metaclass classes
+ accessed = self._accessed.pop()
+ if cnode.type != 'metaclass':
+ self._check_accessed_members(cnode, accessed)
+
+ def visit_function(self, node):
+ """check method arguments, overriding"""
+ # ignore actual functions
+ if not node.is_method():
+ return
+ self._meth_could_be_func = True
+ # check first argument is self if this is actually a method
+ klass = node.parent.frame()
+ self._check_first_arg_for_type(node, klass.type == 'metaclass')
+ if node.name == '__init__':
+ self._check_init(node)
+ return
+ if not isinstance(klass, astng.Class):
+ return
+ # check signature if the method overrload an herited method
+ for overridden in klass.local_attr_ancestors(node.name):
+ # get astng for the searched method
+ try:
+ meth_node = overridden[node.name]
+ except KeyError:
+ # we have found the method but it's not in the local
+ # dictionnary.
+ # This may happen with astng build from living objects
+ continue
+ if not isinstance(meth_node, astng.Function):
+ continue
+ self._check_signature(node, meth_node, 'overridden')
+ break
+ # check if the method overload an attribute
+ try:
+ overridden = klass.instance_attr(node.name)[0] # XXX
+ # we may be unable to get owner class if this is a monkey
+ # patched method
+ while overridden.parent and not isinstance(overridden, astng.Class):
+ overridden = overridden.parent.frame()
+ self.add_message('E0202', args=overridden.name, node=node)
+ except astng.NotFoundError:
+ pass
+
+ pymethods = set(('__new__', '__init__',
+ '__getattr__', '__setattr__',
+ '__hash__', '__cmp__',
+ '__mul__', '__div__', '__add__', '__sub__',
+ '__rmul__', '__rdiv__', '__radd__', '__rsub__',
+ # To be continued
+ ))
+ 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.argnames is not None:
+ self._first_attrs.pop()
+ class_node = node.parent.frame()
+ if (self._meth_could_be_func and node.type == 'method'
+ and not node.name in self.pymethods
+ and not (node.is_abstract() or
+ overrides_a_method(class_node, node.name))
+ and class_node.type != 'interface'):
+ self.add_message('R0201', 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
+ if self._first_attrs and isinstance(node.expr, astng.Name) and \
+ node.expr.name == self._first_attrs[-1]:
+ self._accessed[-1].setdefault(attrname, []).append(node)
+ elif attrname[0] == '_' and not attrname == '_' and not (
+ attrname.startswith('__') and attrname.endswith('__')):
+ # XXX move this in a reusable function
+ klass = node.frame()
+ while klass is not None and not isinstance(klass, astng.Class):
+ if klass.parent is None:
+ klass = None
+ else:
+ klass = klass.parent.frame()
+ # 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()
+ if klass is None or not (callee == klass.name or
+ callee in klass.basenames
+ or (isinstance(node.expr, astng.CallFunc)
+ and isinstance(node.expr.node, astng.Name)
+ and node.expr.node.name == 'super')):
+ self.add_message('W0212', 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 accessed.items():
+ # deactivate "except doesn't do anything", that's expected
+ # pylint: disable-msg=W0704
+## # is it a builtin attribute ?
+## if attr in ('__dict__', '__class__', '__doc__'):
+## # FIXME: old class object doesn't have __class__
+## continue
+ # is it a class attribute ?
+ try:
+ node.local_attr(attr)
+ # yes, stop here
+ continue
+ except astng.NotFoundError:
+ pass
+ # is it an instance attribute of a parent class ?
+ try:
+ node.instance_attr_ancestors(attr).next()
+ # yes, stop here
+ continue
+ except StopIteration:
+ pass
+ # is it an instance attribute ?
+ try:
+ def_nodes = node.instance_attr(attr) # XXX
+ #instance_attribute = True
+ except astng.NotFoundError:
+ pass
+ #instance_attribute = False
+ else:
+ if len(def_nodes) == 1:
+ def_node = def_nodes[0]
+ # check that if the node is accessed in the same method as
+ # it's defined, it's accessed after the initial assigment
+ frame = def_node.frame()
+ lno = def_node.source_line()
+ for _node in nodes:
+ if _node.frame() is frame and _node.lineno < lno:
+ self.add_message('E0203', 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
+ * 'mcs' for a metaclass
+ * not one of the above for a static method
+ """
+ # don't care about functions with unknown argument (builtins)
+ if node.argnames is None:
+ return
+ self._first_attrs.append(node.argnames and node.argnames[0])
+ # static method
+ if node.type == 'staticmethod':
+ if node.argnames and node.argnames[0] in ('self', 'cls', 'mcs'):
+ self.add_message('W0211', args=node.argnames[0], node=node)
+ self._first_attrs[-1] = None
+ # class / regular method with no args
+ elif not node.argnames:
+ self.add_message('E0211', node=node)
+ # metaclass method
+ elif metaclass:
+ if self._first_attrs[-1] != 'mcs':
+ self.add_message('C0203', node=node)
+ # class method
+ elif node.type == 'classmethod':
+ if self._first_attrs[-1] != 'cls':
+ self.add_message('C0202', node=node)
+ # regular method without self as argument
+ elif self._first_attrs[-1] != 'self':
+ self.add_message('E0213', node=node)
+
+ def _check_bases_classes(self, node):
+ """check that the given class node implements abstract methods from
+ base classes
+ """
+ for method in node.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 method.is_abstract(pass_is_abstract=False):
+ self.add_message('W0223', node=node,
+ args=(method.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, astng.Class):
+ e0221_hack[0] = True
+ self.add_message('E0221', 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 begining with an underscore,
+ # usually belonging to the interface implementation
+ continue
+ # get class method astng
+ try:
+ method = node_method(node, name)
+ except astng.NotFoundError:
+ self.add_message('E0222', 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 astng.InferenceError:
+ if e0221_hack[0]:
+ return
+ implements = astng.Instance(node).getattr('__implements__')[0]
+ assignment = implements.parent
+ assert isinstance(assignment, astng.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('F0220', node=implements,
+ args=(node.name, assignment.expr.as_string()))
+
+ def _check_init(self, node):
+ """check that the __init__ method call super or ancestors'__init__
+ method
+ """
+ klass_node = node.parent.frame()
+ to_call = _ancestors_to_call(klass_node)
+ for stmt in node.nodes_of_class(astng.CallFunc):
+ expr = stmt.node
+ if not isinstance(expr, astng.Getattr) \
+ or expr.attrname != '__init__':
+ continue
+ # skip the test if using super
+ if isinstance(expr.expr, astng.CallFunc) and \
+ isinstance(expr.expr.node, astng.Name) and \
+ expr.expr.node.name == 'super':
+ return
+ try:
+ klass = expr.expr.infer().next()
+ if klass is astng.YES:
+ continue
+ try:
+ del to_call[klass]
+ except KeyError:
+ self.add_message('W0233', node=expr, args=klass.name)
+ except astng.InferenceError:
+ continue
+ for klass in to_call.keys():
+ if klass.name == 'object':
+ continue
+ self.add_message('W0231', 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, astng.Function)
+ and isinstance(refmethod, astng.Function)):
+ self.add_message('F0202', args=(method1, refmethod), node=method1)
+ return
+ # don't care about functions with unknown argument (builtins)
+ if method1.argnames is None or refmethod.argnames is None:
+ return
+ if len(method1.argnames) != len(refmethod.argnames):
+ self.add_message('W0221', args=class_type, node=method1)
+ elif len(method1.defaults) < len(refmethod.defaults):
+ self.add_message('W0222', args=class_type, node=method1)
+
+
+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:
+ base_node.local_attr(method)
+ to_call[base_node] = 1
+ except astng.NotFoundError:
+ continue
+ return to_call
+
+
+def node_method(node, method_name):
+ """get astng for <method_name> on the given class node, ensuring it
+ is a Function node
+ """
+ stmt = node.local_attr(method_name)
+ if not isinstance(stmt, astng.Function):
+ raise astng.NotFoundError(method_name)
+ return stmt
+
+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
new file mode 100644
index 0000000..21b4b4a
--- /dev/null
+++ b/checkers/design_analysis.py
@@ -0,0 +1,330 @@
+# Copyright (c) 2003-2006 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.,
+# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+"""check for signs of poor design
+
+
+ see http://intranet.logilab.fr/jpl/view?rql=Any%20X%20where%20X%20eid%201243
+ FIXME: missing 13, 15, 16
+"""
+
+from logilab.astng import Function, InferenceError
+
+from pylint.interfaces import IASTNGChecker
+from pylint.checkers import BaseChecker
+
+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)',
+ 'Used when class has too many parent classes, try to reduce \
+ this to get a more simple (and so easier to use) class.'),
+ 'R0902': ('Too many instance attributes (%s/%s)',
+ 'Used when class has too many instance attributes, try to reduce \
+ this to get a more simple (and so easier to use) class.'),
+ 'R0903': ('Too few public methods (%s/%s)',
+ 'Used when class has too few public methods, so be sure it\'s \
+ really worth it.'),
+ 'R0904': ('Too many public methods (%s/%s)',
+ 'Used when class has too many public methods, try to reduce \
+ this to get a more simple (and so easier to use) class.'),
+
+ 'R0911': ('Too many return statements (%s/%s)',
+ 'Used when a function or method has too many return statement, \
+ making it hard to follow.'),
+ 'R0912': ('Too many branches (%s/%s)',
+ 'Used when a function or method has too many branches, \
+ making it hard to follow.'),
+ 'R0913': ('Too many arguments (%s/%s)',
+ 'Used when a function or method takes too many arguments.'),
+ 'R0914': ('Too many local variables (%s/%s)',
+ 'Used when a function or method has too many local variables.'),
+ 'R0915': ('Too many statements (%s/%s)',
+ 'Used when a function or method has too many statements. You \
+ should then split it in smaller functions / methods.'),
+
+ 'R0921': ('Abstract class not referenced',
+ 'Used when an abstract class is not used as ancestor anywhere.'),
+ 'R0922': ('Abstract class is only referenced %s times',
+ 'Used when an abstract class is used less than X times as \
+ ancestor.'),
+ 'R0923': ('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__ = (IASTNGChecker,)
+
+ # configuration section name
+ name = 'design'
+ # messages
+ msgs = MSGS
+ priority = -2
+ # configuration options
+ options = (('max-args',
+ {'default' : 5, 'type' : 'int', 'metavar' : '<int>',
+ 'help': 'Maximum number of arguments for function / method'}
+ ),
+ ('max-locals',
+ {'default' : 15, 'type' : 'int', 'metavar' : '<int>',
+ 'help': 'Maximum number of locals for function / method body'}
+ ),
+ ('max-returns',
+ {'default' : 6, 'type' : 'int', 'metavar' : '<int>',
+ 'help': 'Maximum number of return / yield for function / '
+ 'method body'}
+ ),
+ ('max-branchs',
+ {'default' : 12, 'type' : 'int', 'metavar' : '<int>',
+ 'help': 'Maximum number of branch for function / method body'}
+ ),
+ ('max-statements',
+ {'default' : 50, 'type' : 'int', 'metavar' : '<int>',
+ 'help': 'Maximum number of statements in function / method '
+ 'body'}
+ ),
+ ('max-parents',
+ {'default' : 7,
+ 'type' : 'int',
+ 'metavar' : '<num>',
+ 'help' : 'Maximum number of parents for a class (see R0901).'}
+ ),
+ ('max-attributes',
+ {'default' : 7,
+ 'type' : 'int',
+ 'metavar' : '<num>',
+ 'help' : 'Maximum number of attributes for a class \
+(see R0902).'}
+ ),
+ ('min-public-methods',
+ {'default' : 2,
+ 'type' : 'int',
+ 'metavar' : '<num>',
+ 'help' : 'Minimum number of public methods for a class \
+(see R0903).'}
+ ),
+ ('max-public-methods',
+ {'default' : 20,
+ 'type' : 'int',
+ 'metavar' : '<num>',
+ '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._branchs = 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._branchs = []
+ self._used_abstracts = {}
+ self._used_ifaces = {}
+ self._abstracts = []
+ self._ifaces = []
+
+ 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('R0921', node=abstract)
+ elif self._used_abstracts[abstract] < 2:
+ self.add_message('R0922', node=abstract,
+ args=self._used_abstracts[abstract])
+ for iface in self._ifaces:
+ if not iface in self._used_ifaces:
+ self.add_message('R0923', node=iface)
+
+ def visit_class(self, node):
+ """check size of inheritance hierarchy and number of instance attributes
+ """
+ self._inc_branch()
+ # 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('R0901', 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('R0902', 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
+
+ def leave_class(self, node):
+ """check number of public methods"""
+ nb_public_methods = 0
+ for method in node.methods():
+ if not method.name.startswith('_'):
+ nb_public_methods += 1
+ # Does the class contain less than 20 public methods ?
+ if nb_public_methods > self.config.max_public_methods:
+ self.add_message('R0904', node=node,
+ args=(nb_public_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 5 public methods ?
+ if nb_public_methods < self.config.min_public_methods:
+ self.add_message('R0903', node=node,
+ args=(nb_public_methods,
+ self.config.min_public_methods))
+
+
+ def visit_function(self, node):
+ """check function name, docstring, arguments, redefinition,
+ variable names, max locals
+ """
+ self._inc_branch()
+ # init branch and returns counters
+ self._returns.append(0)
+ self._branchs.append(0)
+ # check number of arguments
+ args = node.argnames
+ if args is not None and len(args) > self.config.max_args:
+ self.add_message('R0913', node=node,
+ args=(len(args), self.config.max_args))
+ # check number of local variables
+ locnum = len(node.locals)
+ if locnum > self.config.max_locals:
+ self.add_message('R0914', node=node,
+ args=(locnum, self.config.max_locals))
+ # init statements counter
+ self._stmts = 1
+
+ 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('R0911', node=node,
+ args=(returns, self.config.max_returns))
+ branchs = self._branchs.pop()
+ if branchs > self.config.max_branchs:
+ self.add_message('R0912', node=node,
+ args=(branchs, self.config.max_branchs))
+ # check number of statements
+ if self._stmts > self.config.max_statements:
+ self.add_message('R0915', node=node,
+ args=(self._stmts, self.config.max_statements))
+
+ def visit_return(self, _):
+ """count number of returns/yields"""
+ if not self._returns:
+ return # return outside function, reported by the base checker
+ self._returns[-1] += 1
+
+ def visit_yield(self, _):
+ """count number of returns/yields"""
+ if not self._returns:
+ return # yield 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 branchs counter"""
+ branchs = len(node.handlers)
+ if node.else_:
+ branchs += 1
+ self._inc_branch(branchs)
+ self._stmts += branchs
+
+ def visit_tryfinally(self, _):
+ """increments the branchs counter"""
+ self._inc_branch(2)
+ self._stmts += 2
+
+ def visit_if(self, node):
+ """increments the branchs counter"""
+ branchs = len(node.tests)
+ if node.else_:
+ branchs += 1
+ self._inc_branch(branchs)
+ self._stmts += branchs
+
+ def visit_while(self, node):
+ """increments the branchs counter"""
+ branchs = 1
+ if node.else_:
+ branchs += 1
+ self._inc_branch(branchs)
+
+ visit_for = visit_while
+
+ def _inc_branch(self, branchsnum=1):
+ """increments the branchs counter"""
+ branchs = self._branchs
+ for i in xrange(len(branchs)):
+ branchs[i] += branchsnum
+
+ # 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
new file mode 100644
index 0000000..1663915
--- /dev/null
+++ b/checkers/exceptions.py
@@ -0,0 +1,154 @@
+# Copyright (c) 2003-2007 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.,
+# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+"""exceptions handling (raising, catching, exceptions classes) checker
+"""
+
+from logilab.common.compat import enumerate
+from logilab import astng
+from logilab.astng.inference import unpack_infer
+
+from pylint.checkers import BaseChecker
+from pylint.checkers.utils import is_empty, is_raising
+from pylint.interfaces import IASTNGChecker
+
+MSGS = {
+ 'E0701': (
+ 'Bad except clauses order (%s)',
+ '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, instances or string are allowed',
+ 'Used when something which is neither a class, an instance or a \
+ string is raised (i.e. a `TypeError` will be raised).'),
+
+ 'W0701': ('Raising a string exception',
+ 'Used when a string exception is raised.'),
+ 'W0702': ('No exception type(s) specified',
+ 'Used when an except clause doesn\'t specify exceptions type to \
+ catch.'),
+ 'W0703': ('Catch "Exception"',
+ 'Used when an except catches Exception instances.'),
+ 'W0704': ('Except doesn\'t do anything',
+ 'Used when an except clause does nothing but "pass" and there is\
+ no "else" clause.'),
+ 'W0706': (
+ 'Identifier %s used to raise an exception is assigned to %s',
+ 'Used when a variable used to raise an exception is initially \
+ assigned to a value which can\'t be used as an exception.'),
+ }
+
+class ExceptionsChecker(BaseChecker):
+ """checks for
+ * excepts without exception filter
+ * string exceptions
+ """
+
+ __implements__ = IASTNGChecker
+
+ name = 'exceptions'
+ msgs = MSGS
+ priority = -4
+ options = ()
+
+ def visit_raise(self, node):
+ """check for string exception
+ """
+ # ignore empty raise
+ if node.expr1 is None:
+ return
+ expr = node.expr1
+ if isinstance(expr, astng.Const):
+ value = expr.value
+ if isinstance(value, str):
+ self.add_message('W0701', node=node)
+ else:
+ self.add_message('E0702', node=node,
+ args=value.__class__.__name__)
+ elif isinstance(expr, astng.Name) and \
+ expr.name in ('None', 'True', 'False'):
+ self.add_message('E0702', node=node, args=expr.name)
+ elif isinstance(expr, astng.Mod):
+ self.add_message('W0701', node=node)
+ else:
+ try:
+ value = unpack_infer(expr).next()
+ except astng.InferenceError:
+ return
+ # have to be careful since Const, Dict, .. inherit from
+ # Instance now and get the original astng class as _proxied
+ if (value is astng.YES or
+ isinstance(value, (astng.Class, astng.Module)) or
+ (isinstance(value, astng.Instance) and
+ isinstance(value._proxied, astng.Class) and
+ value._proxied.root().name != '__builtin__')):
+ return
+ if isinstance(value, astng.Const) and \
+ (value.value is None or
+ value.value is True or value.value is False):
+ # this Const has been generated by resolve
+ # since None, True and False are represented by Name
+ # nodes in the ast, and so this const node doesn't
+ # have the necessary parent, lineno and so on attributes
+ assinfo = value.as_string()
+ else:
+ assinfo = '%s line %s' % (value.as_string(),
+ value.source_line())
+ self.add_message('W0706', node=node,
+ args=(expr.as_string(), assinfo))
+
+ def visit_tryexcept(self, node):
+ """check for empty except
+ """
+ exceptions_classes = []
+ nb_handlers = len(node.handlers)
+ for index, handler in enumerate(node.handlers):
+ exc_type = handler[0]
+ stmt = handler[2]
+ # single except doing nothing but "pass" without else clause
+ if nb_handlers == 1 and is_empty(stmt) and not node.else_:
+ self.add_message('W0704', node=exc_type or stmt)
+ if exc_type is None:
+ if nb_handlers == 1 and not is_raising(stmt):
+ self.add_message('W0702', node=stmt.nodes[0])
+ # check if a "except:" is followed by some other
+ # except
+ elif index < (nb_handlers - 1):
+ msg = 'empty except clause should always appears last'
+ self.add_message('E0701', node=node, args=msg)
+ else:
+ try:
+ excs = list(unpack_infer(exc_type))
+ except astng.InferenceError:
+ continue
+ for exc in excs:
+ # XXX skip other non class nodes
+ if exc is astng.YES or not isinstance(exc, astng.Class):
+ continue
+ exc_ancestors = [anc for anc in exc.ancestors()
+ if isinstance(anc, astng.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('E0701', node=exc_type, args=msg)
+ if (exc.name == 'Exception'
+ and exc.root().name == 'exceptions'
+ and nb_handlers == 1 and not is_raising(stmt)):
+ self.add_message('W0703', node=exc_type)
+ exceptions_classes += 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
new file mode 100644
index 0000000..41a0fd8
--- /dev/null
+++ b/checkers/format.py
@@ -0,0 +1,343 @@
+# Copyright (c) 2003-2008 Sylvain Thenault (thenault@gmail.com).
+# Copyright (c) 2003-2008 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.,
+# 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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 re
+import tokenize
+if not hasattr(tokenize, 'NL'):
+ raise ValueError("tokenize.NL doesn't exist -- tokenize module too old")
+
+from logilab.common.textutils import pretty_match
+from logilab.astng import nodes
+
+from pylint.interfaces import IRawChecker, IASTNGChecker
+from pylint.checkers import BaseRawChecker
+
+MSGS = {
+ 'C0301': ('Line too long (%s/%s)',
+ 'Used when a line is longer than a given number of characters.'),
+ 'C0302': ('Too many lines in module (%s)', # was W0302
+ 'Used when a module has too much lines, reducing its readibility.'
+ ),
+
+ 'W0311': ('Bad indentation. Found %s %s, expected %s',
+ 'Used when an unexpected number of indentation\'s tabulations or '
+ 'spaces has been found.'),
+ 'W0312': ('Found indentation with %ss instead of %ss',
+ 'Used when there are some mixed tabs and spaces in a module.'),
+
+ 'F0321': ('Format detection error in %r',
+ 'Used when an unexpected error occured in bad format detection.'
+ 'Please report the error if it occurs.'),
+ 'C0321': ('More than one statement on a single line',
+ 'Used when more than on statement are found on the same line.'),
+ 'C0322': ('Operator not preceded by a space\n%s',
+ 'Used when one of the following operator (!= | <= | == | >= | < '
+ '| > | = | \+= | -= | \*= | /= | %) is not preceded by a space.'),
+ 'C0323': ('Operator not followed by a space\n%s',
+ 'Used when one of the following operator (!= | <= | == | >= | < '
+ '| > | = | \+= | -= | \*= | /= | %) is not followed by a space.'),
+ 'C0324': ('Comma not followed by a space\n%s',
+ 'Used when a comma (",") is not followed by a space.'),
+
+ 'W0331': ('Use of the <> operator',
+ 'Used when the deprecated "<>" operator is used instead \
+ of "!=".'),
+ 'W0332': ('Use l as long integer identifier',
+ '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"'),
+ 'W0333': ('Use of the `` operator',
+ 'Used when the deprecated "``" (backtick) operator is used '
+ 'instead of the str() function.'),
+ }
+
+# simple quoted string rgx
+SQSTRING_RGX = r'"([^"\\]|\\.)*("|\\$)'
+# triple quoted string rgx
+TQSTRING_RGX = r'"""([^"]|"(?!""))*("""|$)'
+# simple apostrophed rgx
+SASTRING_RGX = r"'([^'\\]|\\.)*('|\\$)"
+# triple apostrophed string rgx # FIXME english please
+TASTRING_RGX = r"'''([^']|'(?!''))*('''|$)"
+
+# finally, the string regular expression
+STRING_RGX = re.compile('%s|%s|%s|%s' % (TQSTRING_RGX, TASTRING_RGX,
+ SQSTRING_RGX, SASTRING_RGX), re.M)
+
+COMMENT_RGX = re.compile("#.*$", re.M)
+
+OPERATORS = r'!=|<=|==|>=|<|>|=|\+=|-=|\*=|/=|%'
+
+OP_RGX_MATCH_1 = r'[^(]*(?<!\s|\^|<|>|=|\+|-|\*|/|!|%%|&|\|)(%s).*' % OPERATORS
+OP_RGX_SEARCH_1 = r'(?<!\s|\^|<|>|=|\+|-|\*|/|!|%%|&|\|)(%s)' % OPERATORS
+
+OP_RGX_MATCH_2 = r'[^(]*(%s)(?!\s|=|>|<).*' % OPERATORS
+OP_RGX_SEARCH_2 = r'(%s)(?!\s|=|>)' % OPERATORS
+
+BAD_CONSTRUCT_RGXS = (
+## (re.compile(
+## r'\s*(class|def|if|for|while)\s+([^:\[\]]|\[.*:?.*\])*?:\s*\w+(\s|\w)*'),
+## re.compile(r':\s*[^\s]+.*'),
+## 'C0321'),
+
+ (re.compile(OP_RGX_MATCH_1, re.M),
+ re.compile(OP_RGX_SEARCH_1, re.M),
+ 'C0322'),
+
+ (re.compile(OP_RGX_MATCH_2, re.M),
+ re.compile(OP_RGX_SEARCH_2, re.M),
+ 'C0323'),
+
+ (re.compile(r'.*,[^\s)].*', re.M),
+ re.compile(r',[^\s)]', re.M),
+ 'C0324'),
+ )
+
+
+def get_string_coords(line):
+ """return a list of string positions (tuple (start, end)) in the line
+ """
+ result = []
+ for match in re.finditer(STRING_RGX, line):
+ result.append( (match.start(), match.end()) )
+ return result
+
+def in_coords(match, string_coords):
+ """return true if the match in in the string coord
+ """
+ mstart = match.start()
+ for start, end in string_coords:
+ if mstart >= start and mstart < end:
+ return 1
+ return 0
+
+def check_line(line, writer):
+ """check a line for a bad construction
+ if it founds one, return a message describing the problem
+ else return None
+ """
+ clean_str = STRING_RGX.sub('', line)
+ clean_str = COMMENT_RGX.sub('', clean_str)
+ for rgx_match, rgx_search, msg_id in BAD_CONSTRUCT_RGXS:
+ if rgx_match.match(clean_str):
+ string_positions = get_string_coords(line)
+ for match in re.finditer(rgx_search, line):
+ if not in_coords(match, string_positions):
+ return msg_id, pretty_match(match, line.rstrip())
+ writer.add_message('F0321', line=line, args=line)
+
+
+
+class FormatChecker(BaseRawChecker):
+ """checks for :
+ * unauthorized constructions
+ * strict indentation
+ * line length
+ * use of <> instead of !=
+ """
+
+ __implements__ = (IRawChecker, IASTNGChecker)
+
+ # 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' : 80, 'type' : "int", 'metavar' : '<int>',
+ 'help' : 'Maximum number of characters on a single line.'}),
+ ('max-module-lines',
+ {'default' : 1000, 'type' : 'int', 'metavar' : '<int>',
+ 'help': 'Maximum number of lines in a module'}
+ ),
+ ('indent-string',
+ {'default' : ' ', 'type' : "string", 'metavar' : '<string>',
+ 'help' : 'String used as indentation unit. This is usually \
+" " (4 spaces) or "\\t" (1 tab).'}),
+ )
+ def __init__(self, linter=None):
+ BaseRawChecker.__init__(self, linter)
+ self._lines = None
+ self._visited_lines = None
+
+ def new_line(self, tok_type, line, line_num, junk):
+ """a new line has been encountered, process it if necessary"""
+ if not tok_type in junk:
+ self._lines[line_num] = line
+ self.check_lines(line, line_num)
+
+ def process_tokens(self, tokens):
+ """process tokens and search for :
+
+ _ non strict indentation (i.e. not always using the <indent> parameter as
+ indent unit)
+ _ too long lines (i.e. longer than <max_chars>)
+ _ optionally bad construct (if given, bad_construct must be a compiled
+ regular expression).
+ """
+ indent = tokenize.INDENT
+ dedent = tokenize.DEDENT
+ newline = tokenize.NEWLINE
+ junk = (tokenize.COMMENT, tokenize.NL)
+ indents = [0]
+ check_equal = 0
+ line_num = 0
+ self._lines = {}
+ self._visited_lines = {}
+ for (tok_type, token, start, _, line) in tokens:
+ if start[0] != line_num:
+ line_num = start[0]
+ self.new_line(tok_type, line, line_num, junk)
+ if tok_type == tokenize.OP:
+ if token == '<>':
+ self.add_message('W0331', line=line_num)
+ elif tok_type == tokenize.NUMBER:
+ if token.endswith('l'):
+ self.add_message('W0332', line=line_num)
+
+ elif tok_type == 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 = 1
+
+ elif tok_type == indent:
+ check_equal = 0
+ self.check_indent_level(token, indents[-1]+1, line_num)
+ indents.append(indents[-1]+1)
+
+ elif tok_type == 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 = 1
+ if len(indents) > 1:
+ del indents[-1]
+
+ elif check_equal and tok_type not in junk:
+ # this is the first "real 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
+ check_equal = 0
+ self.check_indent_level(line, indents[-1], line_num)
+ line_num -= 1 # to be ok with "wc -l"
+ if line_num > self.config.max_module_lines:
+ self.add_message('C0302', args=line_num, line=1)
+
+ def visit_default(self, node):
+ """check the node line number and check it if not yet done
+ """
+ if not node.is_statement():
+ return
+ prev_sibl = node.previous_sibling()
+ if prev_sibl is not None:
+ # don't use .source_line since it causes C0321 false positive !
+ prev_line = prev_sibl.fromlineno
+ # discard discard nodes introducted by ending ";"
+ if isinstance(node, nodes.Discard) and \
+ isinstance(node.expr, nodes.Const) and \
+ node.expr.lineno is None:
+ prev_line = None
+ else:
+ # itou ?
+ try:
+ prev_line = node.parent.statement().fromlineno
+ except AttributeError:
+ prev_line = node.parent.statement().lineno
+ line = node.fromlineno #source_line()
+ if prev_line == line and self._visited_lines.get(line) != 2:
+ self.add_message('C0321', node=node)
+ self._visited_lines[line] = 2
+ return
+ if self._visited_lines.has_key(line):
+ return
+ lines = []
+ for line in xrange(node.fromlineno, node.tolineno + 1):
+ self._visited_lines[line] = 1
+ try:
+ lines.append(self._lines[line].rstrip())
+ except KeyError:
+ lines.append('')
+ #print 'check', '\n'.join(lines)
+ #print node
+ try:
+ msg_def = check_line('\n'.join(lines), self)
+ if msg_def:
+ self.add_message(msg_def[0], node=node, args=msg_def[1])
+ except KeyError:
+ # FIXME: internal error !
+ pass
+
+ def visit_backquote(self, node):
+ self.add_message('W0333', node=node)
+
+ def check_lines(self, lines, i):
+ """check lines have less than a maximum number of characters
+ """
+ max_chars = self.config.max_line_length
+ for line in lines.splitlines():
+ if len(line) > max_chars:
+ self.add_message('C0301', 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('W0312', 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('W0311', 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
new file mode 100644
index 0000000..b6825ba
--- /dev/null
+++ b/checkers/imports.py
@@ -0,0 +1,393 @@
+# Copyright (c) 2003-2008 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.,
+# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+"""imports checkers for Python code
+"""
+
+from logilab.common.graph import get_cycles
+from logilab.common.modutils import is_standard_module, is_relative, \
+ get_module_part
+from logilab.common.ureports import VerbatimText, Paragraph
+
+from logilab import astng
+
+from pylint.interfaces import IASTNGChecker
+from pylint.checkers import BaseChecker, EmptyReport
+from pylint.checkers.utils import are_exclusive
+
+def get_first_import(context, name, base, level=0):
+ """return the node where [base.]<name> is imported or None if not found
+ """
+ for node in context.values():
+ if isinstance(node, astng.Import):
+ if name in [iname[0] for iname in node.names]:
+ return node
+ if isinstance(node, astng.From):
+ if base == node.modname and level == node.level and \
+ name in [iname[0] for iname in node.names]:
+ return node
+
+
+# utilities to represents import dependencies as tree and dot graph ###########
+
+def filter_dependencies_info(dep_info, package_dir, mode='external'):
+ """filter external or internal dependencies from dep_info (return a
+ new dictionary containing the filtered modules only)
+ """
+ if mode == 'external':
+ filter_func = lambda x: not is_standard_module(x, (package_dir,))
+ else:
+ assert mode == 'internal'
+ filter_func = lambda x: is_standard_module(x, (package_dir,))
+ result = {}
+ for importee, importers in dep_info.items():
+ if filter_func(importee):
+ result[importee] = importers
+ return result
+
+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 dictionnary 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 in range(len(nodes)):
+ mod, (sub, files) = nodes[i]
+ 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('%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 dot_node(modname):
+ """return the string representation for a dot node"""
+ return '"%s" [ label="%s" ];' % (modname, modname)
+
+def dot_edge(from_, to_):
+ """return the string representation for a dot edge between two nodes"""
+ return'"%s" -> "%s" [ ] ;' % (from_, to_)
+
+DOT_HEADERS = '''rankdir="LR" URL="." concentrate=false
+edge[fontsize="10" ]
+node[width="0" height="0" fontsize="12" fontcolor="black"]'''
+
+def dependencies_graph(filename, dep_info):
+ """write dependencies as defined in the dep_info dictionary as a dot
+ (graphviz) file
+ """
+ done = {}
+ stream = open(filename, 'w')
+ print >> stream, "digraph g {"
+ print >> stream, DOT_HEADERS
+ for modname, dependencies in dep_info.items():
+ done[modname] = 1
+ print >> stream, dot_node(modname)
+ for modname in dependencies:
+ if not done.has_key(modname):
+ done[modname] = 1
+ print >> stream, dot_node(modname)
+ for depmodname, dependencies in dep_info.items():
+ for modname in dependencies:
+ print >> stream, dot_edge(modname, depmodname)
+ print >> stream,'}'
+ stream.close()
+
+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 %r (%s)' ,
+ 'Used when pylint has been unable to import a module.'),
+ 'R0401': ('Cyclic import (%s)',
+ 'Used when a cyclic import between two or more modules is \
+ detected.'),
+
+ 'W0401': ('Wildcard import %s',
+ 'Used when `from module import *` is detected.'),
+ 'W0402': ('Uses of a deprecated module %r',
+ 'Used a module marked as deprecated is imported.'),
+ 'W0403': ('Relative import %r',
+ 'Used when an import relative to the package directory is \
+ detected.'),
+ 'W0404': ('Reimport %r (imported line %s)',
+ 'Used when a module is reimported multiple times.'),
+ 'W0406': ('Module import itself',
+ 'Used when a module is importing itself.'),
+
+ 'W0410': ('__future__ import is not the first non docstring statement',
+ 'Python 2.5 and greater require __future__ import to be the \
+ first non docstring statement in the module.'),
+ }
+
+class ImportsChecker(BaseChecker):
+ """checks for
+ * external modules dependencies
+ * relative / wildcard imports
+ * cyclic imports
+ * uses of deprecated modules
+ """
+
+ __implements__ = IASTNGChecker
+
+ name = 'imports'
+ msgs = MSGS
+ priority = -2
+
+ options = (('deprecated-modules',
+ {'default' : ('regsub','string', 'TERMIOS',
+ 'Bastion', 'rexec'),
+ 'type' : 'csv',
+ 'metavar' : '<modules>',
+ 'help' : 'Deprecated modules which should not be used, \
+separated by a comma'}
+ ),
+ ('import-graph',
+ {'default' : '',
+ 'type' : 'string',
+ 'metavar' : '<file.dot>',
+ 'help' : 'Create a graph of every (i.e. internal and \
+external) dependencies in the given file (report R0402 must not be disabled)'}
+ ),
+ ('ext-import-graph',
+ {'default' : '',
+ 'type' : 'string',
+ 'metavar' : '<file.dot>',
+ 'help' : 'Create a graph of external dependencies in the \
+given file (report R0402 must not be disabled)'}
+ ),
+ ('int-import-graph',
+ {'default' : '',
+ 'type' : 'string',
+ 'metavar' : '<file.dot>',
+ 'help' : 'Create a graph of internal dependencies in the \
+given file (report R0402 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 = (('R0401', 'External dependencies',
+ self.report_external_dependencies),
+ ('R0402', '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 = {}
+
+ 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('R0401'):
+ for cycle in get_cycles(self.import_graph):
+ self.add_message('R0401', args=' -> '.join(cycle))
+
+ def visit_import(self, node):
+ """triggered when an import statement is seen"""
+ for name, _ in node.names:
+ self._check_deprecated(node, name)
+ relative = self._check_relative(node, name)
+ self._imported_module(node, name, relative)
+ # handle reimport
+ self._check_reimport(node, name)
+
+
+ def visit_from(self, node):
+ """triggered when a from statement is seen"""
+ basename = node.modname
+ if basename == '__future__':
+ # check 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, astng.From)
+ and prev.modname == '__future__'):
+ self.add_message('W0410', node=node)
+ self._check_deprecated(node, basename)
+ level = node.level
+ if level > 0: # explicit relative import (leading dots)
+ relative = True
+ else:
+ relative = self._check_relative(node, basename)
+ for name, _ in node.names:
+ if name == '*':
+ self.add_message('W0401', args=basename, node=node)
+ continue
+ # handle reimport
+ self._check_reimport(node, name, basename, level)
+ # analyze dependencies
+ fullname = '.' * level + '%s.%s' % (basename, name)
+ try:
+ # XXXFIXME: don't use get_module_part which doesn't take
+ # care of package precedence
+ fullname = get_module_part(fullname,
+ context_file=node.root().file)
+ except ImportError, ex:
+ self.add_message('F0401', args=(fullname, ex), node=node)
+ continue
+ self._imported_module(node, fullname, relative)
+
+ def _imported_module(self, node, mod_path, relative):
+ """notify an imported module, used to analyze dependencies
+ """
+ context_name = node.root().name
+ if relative:
+ context_parts = context_name.split('.')
+ if mod_path.startswith('.'):
+ while mod_path.startswith('.'):
+ mod_path = mod_path[1:]
+ del context_parts[-1] # one level upwards
+ context_parts.append(mod_path)
+ else:
+ context_parts[-1] = mod_path
+ mod_path = '.'.join(context_parts)
+ if context_name == mod_path:
+ # module importing itself !
+ self.add_message('W0406', node=node)
+ elif not is_standard_module(mod_path):
+ # handle dependencies
+ mod_paths = self.stats['dependencies'].setdefault(mod_path, [])
+ if not context_name in mod_paths:
+ mod_paths.append(context_name)
+ if is_standard_module( mod_path, (self.package_dir(),) ):
+ # update import graph
+ mgraph = self.import_graph.setdefault(context_name, [])
+ if not mod_path in mgraph:
+ mgraph.append(mod_path)
+
+ def _check_relative(self, node, mod_path):
+ """check relative import module"""
+ # check for relative import
+ context_file = node.root().file
+ relative = is_relative(mod_path, context_file)
+ if relative:
+ self.add_message('W0403', args=mod_path, node=node)
+ return relative
+
+ def _check_deprecated(self, node, mod_path):
+ """check if the module is deprecated"""
+ for mod_name in self.config.deprecated_modules:
+ if mod_path.startswith(mod_name) and \
+ (len(mod_path) == len(mod_name)
+ or mod_path[len(mod_name)] == '.'):
+ self.add_message('W0402', node=node, args=mod_path)
+
+ def _check_reimport(self, node, name, basename=None, level=0):
+ """check if the import is necessary (i.e. not already done)
+ """
+ frame = node.frame()
+ first = get_first_import(frame, name, basename, level)
+ if isinstance(first, (astng.Import, astng.From)) and first is not node \
+ and not are_exclusive(first, node):
+ self.add_message('W0404', node=node, args=(name, first.lineno))
+ else:
+ root = node.root()
+ if root is frame:
+ return
+ first = get_first_import(root, name, basename)
+ if not isinstance(first, (astng.Import, astng.From)):
+ return
+ if first is not node and not are_exclusive(first, node):
+ self.add_message('W0404', node=node,
+ args=(name, first.lineno))
+
+
+ def report_external_dependencies(self, sect, _, dummy):
+ """return a verbatim layout for displaying dependencies
+ """
+ dep_info = make_tree_defs(self._external_dependencies_info().items())
+ 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:
+ self.__ext_dep_info = filter_dependencies_info(
+ self.stats['dependencies'], self.package_dir(), 'external')
+ 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:
+ self.__int_dep_info = filter_dependencies_info(
+ self.stats['dependencies'], self.package_dir(), 'internal')
+ return self.__int_dep_info
+
+
+def register(linter):
+ """required method to auto register this checker """
+ linter.register_checker(ImportsChecker(linter))
diff --git a/checkers/misc.py b/checkers/misc.py
new file mode 100644
index 0000000..9160645
--- /dev/null
+++ b/checkers/misc.py
@@ -0,0 +1,127 @@
+# pylint: disable-msg=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.,
+# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+""" Copyright (c) 2000-2003 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)
+"""
+
+__revision__ = '$Id: misc.py,v 1.19 2005-11-02 09:21:47 syt Exp $'
+
+import re
+
+from pylint.interfaces import IRawChecker
+from pylint.checkers import BaseChecker
+
+def is_ascii(string):
+ """return true if non ascii characters are detected in the given string
+ """
+ if string:
+ return max([ord(char) for char in string]) < 128
+ return True
+
+# regexp matching both emacs and vim declaration
+ENCODING_RGX = re.compile("[^#]*#*.*coding[:=]\s*([^\s]+)")
+
+def guess_encoding(string):
+ """try to guess encoding from a python file as string
+ return None if not found
+ """
+ assert type(string) is type(''), type(string)
+ # check for UTF-8 byte-order mark
+ if string.startswith('\xef\xbb\xbf'):
+ return 'UTF-8'
+ first_lines = string.split('\n', 2)[:2]
+ for line in first_lines:
+ # check for emacs / vim encoding declaration
+ match = ENCODING_RGX.match(line)
+ if match is not None:
+ return match.group(1)
+
+
+MSGS = {
+ 'E0501': ('Non ascii characters found but no encoding specified (PEP 263)',
+ 'Used when some non ascii characters are detected but now \
+ encoding is specified, as explicited in the PEP 263.'),
+ 'E0502': ('Wrong encoding specified (%s)',
+ 'Used when a known encoding is specified but the file doesn\'t \
+ seem to be actually in this encoding.'),
+ 'E0503': ('Unknown encoding specified (%s)',
+ 'Used when an encoding is specified, but it\'s unknown to Python.'
+ ),
+
+ 'W0511': ('%s',
+ 'Used when a warning note as FIXME or XXX is detected.'),
+ }
+
+class EncodingChecker(BaseChecker):
+ """checks for:
+ * warning notes in the code like FIXME, XXX
+ * PEP 263: source code with non ascii character but no encoding declaration
+ """
+ __implements__ = IRawChecker
+
+ # configuration section name
+ name = 'miscellaneous'
+ msgs = MSGS
+
+ options = (('notes',
+ {'type' : 'csv', 'metavar' : '<comma separated values>',
+ 'default' : ('FIXME', 'XXX', 'TODO'),
+ 'help' : 'List of note tags to take in consideration, \
+separated by a comma.'
+ }),
+ )
+
+ def __init__(self, linter=None):
+ BaseChecker.__init__(self, linter)
+
+ def process_module(self, stream):
+ """inspect the source file to found encoding problem or fixmes like
+ notes
+ """
+ # source encoding
+ data = stream.read()
+ if not is_ascii(data):
+ encoding = guess_encoding(data)
+ if encoding is None:
+ self.add_message('E0501', line=1)
+ else:
+ try:
+ unicode(data, encoding)
+ except UnicodeError:
+ self.add_message('E0502', args=encoding, line=1)
+ except LookupError:
+ self.add_message('E0503', args=encoding, line=1)
+ del data
+ # warning notes in the code
+ stream.seek(0)
+ notes = []
+ for note in self.config.notes:
+ notes.append(re.compile(note))
+ linenum = 1
+ for line in stream.readlines():
+ for note in notes:
+ match = note.search(line)
+ if match:
+ self.add_message('W0511', args=line[match.start():-1],
+ line=linenum)
+ break
+ linenum += 1
+
+
+
+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
new file mode 100644
index 0000000..9832a72
--- /dev/null
+++ b/checkers/newstyle.py
@@ -0,0 +1,148 @@
+# Copyright (c) 2005-2006 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.,
+# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+"""check for new / old style related problems
+"""
+
+import sys
+
+from logilab import astng
+
+from pylint.interfaces import IASTNGChecker
+from pylint.checkers import BaseChecker
+
+MSGS = {
+ 'E1001': ('Use __slots__ on an old style class',
+ 'Used when an old style class use the __slots__ attribute.'),
+ 'E1002': ('Use super on an old style class',
+ 'Used when an old style class use the super builtin.'),
+ 'E1003': ('Bad first argument %r given to super class',
+ 'Used when another argument than the current class is given as \
+ first argument of the super builtin.'),
+ 'E1010': ('Raising a new style class',
+ 'Used when a new style class is raised since it\'s not \
+ possible with python < 2.5.'),
+
+ 'W1001': ('Use of "property" on an old style 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'),
+ 'W1010': ('Exception doesn\'t inherit from standard "Exception" class',
+ 'Used when a custom exception class is raised but doesn\'t \
+ inherit from the builtin "Exception" class.'),
+ }
+
+
+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
+ * raising a new style class as exception
+ """
+
+ __implements__ = (IASTNGChecker,)
+
+ # configuration section name
+ name = 'newstyle'
+ # messages
+ msgs = MSGS
+ priority = -2
+ # configuration options
+ options = ()
+
+# def __init__(self, linter=None):
+# BaseChecker.__init__(self, linter)
+
+ def visit_class(self, node):
+ """check __slots__ usage
+ """
+ if '__slots__' in node and not node.newstyle:
+ self.add_message('E1001', node=node)
+
+ def visit_callfunc(self, node):
+ """check property usage"""
+ parent = node.parent.frame()
+ if (isinstance(parent, astng.Class) and
+ not parent.newstyle and
+ isinstance(node.node, astng.Name)):
+ name = node.node.name
+ if name == 'property':
+ self.add_message('W1001', node=node)
+
+ def visit_raise(self, node):
+ """check for raising new style class
+ """
+ # ignore empty raise
+ if node.expr1 is None:
+ return
+ if not isinstance(node.expr1, (astng.Const, astng.Mod)):
+ try:
+ name = node.expr1.nodes_of_class(astng.Name).next()
+ value = name.infer().next()
+ except (StopIteration, astng.ResolveError):
+ pass
+ else:
+ if isinstance(value, astng.Class):
+ if value.newstyle and sys.version_info < (2, 5):
+ self.add_message('E1010', node=node)
+ elif not inherit_from_std_ex(value):
+ self.add_message('W1010', node=node)
+
+ 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(astng.CallFunc):
+ expr = stmt.node
+ if not isinstance(expr, astng.Getattr):
+ continue
+ call = expr.expr
+ # skip the test if using super
+ if isinstance(call, astng.CallFunc) and \
+ isinstance(call.node, astng.Name) and \
+ call.node.name == 'super':
+ if not klass.newstyle:
+ # super should not be used on an old style class
+ self.add_message('E1002', node=node)
+ else:
+ # super first arg should be the class
+ try:
+ supcls = (call.args and call.args[0].infer().next()
+ or None)
+ except astng.InferenceError:
+ continue
+ if klass is not supcls:
+ supcls = getattr(supcls, 'name', supcls)
+ self.add_message('E1003', node=node, args=supcls)
+
+
+
+def inherit_from_std_ex(node):
+ """return true if the given class node is subclass of
+ exceptions.Exception
+ """
+ if node.name == 'Exception' and node.root().name == 'exceptions':
+ return True
+ for parent in node.ancestors(recurs=False):
+ if inherit_from_std_ex(parent):
+ return True
+ return False
+
+def register(linter):
+ """required method to auto register this checker """
+ linter.register_checker(NewStyleConflictChecker(linter))
diff --git a/checkers/raw_metrics.py b/checkers/raw_metrics.py
new file mode 100644
index 0000000..4771207
--- /dev/null
+++ b/checkers/raw_metrics.py
@@ -0,0 +1,128 @@
+# 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.,
+# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+""" Copyright (c) 2003-2005 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 IRawChecker
+from pylint.checkers import BaseRawChecker, EmptyReport
+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(BaseRawChecker):
+ """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__ = (IRawChecker,)
+
+ # configuration section name
+ name = 'metrics'
+ # configuration options
+ options = ( )
+ # messages
+ msgs = {}
+ # reports
+ reports = ( ('R0701', 'Raw metrics', report_raw_stats), )
+
+ def __init__(self, linter):
+ BaseRawChecker.__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/rpython.py b/checkers/rpython.py
new file mode 100644
index 0000000..e2239d5
--- /dev/null
+++ b/checkers/rpython.py
@@ -0,0 +1,448 @@
+# Copyright (c) 2007 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.,
+# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+"""check a python program is `Restricted Python`_ compliant. It is intended to
+find potential pypy translation bugs at once without waiting a long time to get
+translation failures one by one.
+"""
+
+__docformat__ = "restructuredtext en"
+
+import re
+
+from logilab.common.compat import set
+from logilab import astng
+
+from pylint.interfaces import IASTNGChecker
+from pylint.checkers import BaseChecker
+
+
+MSGS = {
+ 'E1201': ('Using unavailable keyword %r',
+ 'Used when a keyword which is not available in rpython is used.'),
+ 'E1202': ('Using unavailable builtin %r',
+ 'Used when a built-in which is not available in rpython is used.'
+ ),
+ 'E1203': ('generator expressions are not supported',
+ 'Used when a generator expression is used while generator are \
+not available in rpython.'),
+ 'E1204': ('multiple inheritance only supported under specific rules \
+which doesn\'t seems to be satisfied',
+ 'Multiple inheritance is only supported using pure mixin (no \
+instance attribute defined in the mix-in) with "_mixin_" class attribute set \
+to True.'),
+ 'E1205': ('Using unavailable protocol %r',
+ 'Used when a special method not handled by rpython is used *and*'
+ ' that may not be used explicitly is used (see W1201).'),
+
+
+ 'E1210': ('Multiple types assigned to %s %r',
+ 'Used when an identifier or attribut is infered as having values'
+ ' of different types assigned.'),
+ 'E1211': ('Can\'t mix %s and None on %s %r',
+ 'Used when an int or float variable is assigned to None.'),
+ 'E1212': ('Non homogeneous values in list',
+ 'Used when a list is not containing homogeneous values.'),
+
+
+## 'E1205': ('Identifier %r is not properly initialized',
+## 'Used when an identifier is used in some conditional block \
+## without being properly initialized before that block.'),
+
+ 'E1220': ('Modifying global %r from module %s',
+ 'Used when a module variable is modified, which is not allowed '
+ 'in rpython since globals are considered as constants.'),
+
+
+ 'E1230': ('Using negative slice %s %s (infered to %s)',
+ 'Used when a negative integer is used as lower, upper or step of'
+ ' a slice.'),
+ 'E1231': ('Using non constant step',
+ 'Used when a variable not annotated as a constant is used as '
+ 'step of a slice.'),
+
+ 'E1240': ('%r format is not supported',
+ 'Used when the unavailable "%r" formatting instruction is used.'
+ ),
+
+ 'W1201': ('special method %s has to be called explicitly',
+ 'Used when a special method is defined on a class, as rpython '
+ 'won\'t call the explicitly.'),
+
+ }
+
+# XXX: nested functions/classes
+# XXX: properties not supported ?
+
+# 'global' is available even if it doesn't help anything since globals are
+# considered immutable
+UNAVAILABLE_KEYWORDS = set(('yield', 'exec', 'lambda', 'print'))
+
+import __builtin__
+BUILTINLIST = set([x for x in dir(__builtin__) if x[0].islower()])
+AUTHORIZED = set(('abs', 'apply',
+ 'basestring', 'bool',
+ 'chr', 'cmp', 'coerce',
+ 'float', 'hasattr', 'hash', 'hex',
+ 'int', 'isinstance',
+ 'len', 'list', 'max', 'min', 'oct', 'ord',
+ 'range', 'slice', 'str', 'tuple', 'type',
+ 'unichr', 'xrange', 'zip'
+ ))
+
+UNAVAILABLE_BUILTINS = {
+ '__builtin__': BUILTINLIST - AUTHORIZED,
+ 'site': 'help',
+ }
+#from pprint import pprint
+#pprint(sorted(BUILTINLIST - AUTHORIZED))
+del BUILTINLIST, AUTHORIZED
+
+BUILTIN_MODIFIERS = {'dict': set(('clear', 'fromkeys', 'pop', 'popitem',
+ 'setdefault', 'update')),
+ 'list': set(('append', 'extend', 'insert', 'pop',
+ 'remove', 'reverse', 'sort')),}
+
+UNAVAILABLE_PROTOCOLS = set(('__new__',))
+
+REPR_NAMED_FORMAT_INSTR = re.compile('%\([^)]+\)r')
+
+
+
+def is_pure_mixin(node):
+ """return true if the given class node can be considered as a mixin class
+ according to rpython conventions
+ """
+ if node.instance_attrs:
+ return False
+ try:
+ for infered in node.igetattr('_mixin_'):
+ if isinstance(infered, astng.Const) and infered.value:
+ return True
+ except astng.InferenceError:
+ return False
+
+
+class RPythonChecker(BaseChecker):
+ """check a python program is `Restricted Python`_ compliant. Restricted
+ python is used in the PyPy_ project to make a python program compilable.
+
+ .. _`Restricted Python`: http://codespeak.net/pypy/dist/pypy/doc/coding-guide.html
+ .. _`PyPy`: http://codespeak.net/pypy/
+ """
+
+ __implements__ = (IASTNGChecker,)
+
+ # configuration section name
+ name = 'rpython'
+ enabled = False # disabled by default
+ # messages
+ msgs = MSGS
+ priority = -1
+ # configuration options
+ options = ()
+
+
+ def __init__(self, linter=None):
+ BaseChecker.__init__(self, linter)
+ self._rpython = True
+
+
+ def visit_name(self, node):
+ """check unavailable builtins are not used"""
+ if not self._rpython:
+ return
+ try:
+ infered = node.infer().next()
+ except astng.InferenceError:
+ return # XXX
+ if infered is astng.YES:
+ return # XXX
+ name = node.name
+ module = infered.root().name
+ if module in UNAVAILABLE_BUILTINS and \
+ name in UNAVAILABLE_BUILTINS[module]:
+ self.add_message('E1202', node=node, args=name)
+## # E1205 check, example:
+## #
+## # ...
+## # if bla:
+## # a = 4
+## # else:
+## # a = 5
+## # print a
+## #
+## # in such a case a should be defined before the if/else block.
+## # So here if name is a local name we have to ckeck it's defined in
+## # the same block or in a parent block
+## frame, stmts = node.lookup(name)
+## if frame is node.frame() and len(stmts) > 1:
+## # XXX only consider the first assignment ?
+## assstmt = stmts[0].statement().parent
+## _node = node.statement()
+## while _node:
+## if _node is assstmt:
+## break
+## _node = _node.parent
+## else:
+## self.add_message('E1205', node=node, args=name)
+ try:
+ for infered in node.infer():
+ if infered is astng.YES:
+ continue
+ break
+ else:
+ return
+ except astng.InferenceError:
+ return
+ frame = infered.frame()
+ # check for more global alteration
+ if not frame is node.frame() and isinstance(frame, astng.Module):
+ # accessing to a global
+ if isinstance(infered, (astng.List, astng.Dict)):
+ # is it a tuple/dict item assignment ?
+ ass = node.parent
+ last = node
+ while ass and not isinstance(ass, astng.Assign):
+ last = last
+ ass = ass.parent
+ # "not last is ass.expr" is checking the global isn't in the rhs
+ if ass is not None and not last is ass.expr:
+ self.add_message('E1220', node=node,
+ args=(node.name, node.root().name))
+ return
+ # is it a call to a tuple/dict method modifying it ?
+ if isinstance(node.parent, astng.Getattr) and \
+ isinstance(node.parent.parent, astng.CallFunc):
+ if node.parent.attrname in BUILTIN_MODIFIERS[infered.name]:
+ self.add_message('E1220', node=node,
+ args=(node.name, node.root().name))
+
+ def visit_class(self, node):
+ """check class attributes have homogeneous types"""
+ if not self._rpython:
+ return
+ for name in node.instance_attrs.keys():
+ self.check_types(node, name, astng.Instance(node).igetattr(name),
+ 'attribute')
+ # XXX recurs ?
+ ancestors = list(node.ancestors(recurs=False))
+ if len(ancestors) > 1:
+ for parent in ancestors[:]:
+ if is_pure_mixin(parent):
+ ancestors.remove(parent)
+ if len(ancestors) > 1:
+ self.add_message('E1204', node=node)
+
+ def visit_function(self, node):
+ """check function locals have homogeneous types"""
+ docstring = node.doc
+ if docstring is not None and 'NOT RPYTHON' in docstring:
+ self._rpython = False
+ if node.name in UNAVAILABLE_PROTOCOLS:
+ self.add_message('E1205', node=node, args=node.name)
+ elif node.name.startswith('__') and node.name.endswith('__') and \
+ node.name != '__init__':
+ self.add_message('W1201', node=node, args=node.name)
+
+ if not self._rpython:
+ return
+ for name in node.locals.keys():
+ self.check_types(node, name, node.ilookup(name), 'identifier')
+
+ def leave_function(self, node):
+ docstring = node.doc
+ if docstring is not None and 'NOT RPYTHON' in docstring:
+ self._rpython = True
+
+ def visit_list(self, node):
+ """check list contains homogeneous types"""
+ if not self._rpython:
+ return
+ types = set()
+ for node in node.nodes:
+ try:
+ # XXX use ifilter + filter to factorize filtering below
+ for infered in node.infer():
+ if infered is astng.YES:
+ continue
+ # XXX skip None ?
+ if isinstance(infered, astng.Const) and \
+ infered.value is None:
+ continue
+ types.add(str(infered))
+ except astng.InferenceError:
+ continue
+ if len(types) > 1:
+ self.add_message('E1212', node=node)
+
+
+ def visit_assattr(self, node):
+ """check we are not modifying a module attribute"""
+ if not self._rpython:
+ return
+ try:
+ infered = node.expr.infer().next()
+ except astng.InferenceError:
+ return # XXX
+ if isinstance(infered, astng.Module):
+ self.add_message('E1220', node=node,
+ args=(node.attrname, infered.name))
+
+ def visit_assname(self, node):
+ """check we are not modifying a module attribute"""
+ if not self._rpython:
+ return
+ frame = node.frame()
+ if not node.name in node.frame().locals:
+ self.add_message('E1220', node=node,
+ args=(node.name, node.root().name))
+
+
+ def visit_slice(self, node):
+ """no negative index"""
+ if not self._rpython:
+ return
+ self.check_slice(node.lower, node.upper)
+
+ def visit_sliceobj(self, node):
+ """no negative index"""
+ if not self._rpython:
+ return
+ sdef = []
+ for bound in node.nodes:
+ if isinstance(bound, astng.Const) and bound.value is None:
+ sdef.append(None)
+ else:
+ sdef.append(bound)
+ self.check_slice(*sdef)
+
+ def visit_genexpr(self, node):
+ self.add_message('E1203', node=node)
+ # avoid E1220 false positive due to particular gen expr variable scope
+ self._rpython = False
+
+ def leave_genexpr(self, node):
+ self._rpython = True
+
+ def visit_mod(self, node):
+ try:
+ for infered in node.left.infer():
+ if infered is astng.YES:
+ continue
+ if not (isinstance(infered, astng.Const) and
+ isinstance(infered.value, basestring)):
+ self.add_message('F0004', node=node, args=infered)
+ continue
+ value = infered.value.replace('%%', '%%')
+ if '%r' in value or REPR_NAMED_FORMAT_INSTR.search(value):
+ self.add_message('E1240', node=infered)
+
+ except astng.InferenceError:
+ pass
+
+
+ def check_types(self, node, name, inferednodes, vtype):
+ """check types assigned to a name (vtype is a string telling if it's a
+ local or attribute
+
+ node is the starting node (function or class node usually)
+ infered the infered value for the name
+ """
+ types = set()
+ hasnone = False
+ for infered in inferednodes:
+ if infered is astng.YES:
+ continue
+ # skip None
+ if isinstance(infered, astng.Const) and infered.value is None:
+ hasnone = True
+ continue
+ types.add(infered.pytype())
+ if len(types) > 1:
+ self.add_message('E1210', node=node, args=(vtype, name))
+ elif hasnone and types:
+ ptype = types.pop()
+ # XXX long ? they should not been supported but this is not handled
+ # do that in visit_const ?
+ if ptype in ('__builtin__.int', '__builtin__.float'):
+ ptype = ptype.split('.')[1]
+ self.add_message('E1211', node=node, args=(ptype, vtype, name))
+
+ def check_slice(self, start, stop, step=None):
+ """
+ * step has to be annotated as a constant and >= 0
+ * start >= 0
+ * stop >= 0
+ * [:-1] et [0:-1] OK
+ """
+
+ if start is not None:
+ value = self.check_positive_integer(start, 'start index')
+ if stop is not None:
+ self.check_positive_integer(stop, 'stop index',
+ start is None or value == 0)
+ if step is not None:
+ try:
+ for infered in step.infer():
+ if infered is astng.YES:
+ self.add_message('E1231', node=step)
+ return
+ except astng.InferenceError:
+ self.add_message('E1231', node=step)
+ return
+ self.check_positive_integer(step, 'step')
+
+ def check_positive_integer(self, node, msg, minus_one_allowed=False):
+ value = None
+ try:
+ for infered in node.infer():
+ if infered is astng.YES:
+ continue
+ if not (isinstance(infered, astng.Const) and
+ isinstance(infered.value, int)):
+ self.add_message('F0004', node=node, args=infered)
+ continue
+ if infered.value < 0:
+ if minus_one_allowed and infered.value == -1:
+ value = infered.value
+ continue
+ self.add_message('E1230', node=node,
+ args=(msg, node.as_string(),
+ infered.value))
+ else:
+ value = infered.value
+ except astng.InferenceError:
+ pass
+ return value
+
+
+
+# XXX: checking rpython should do an "entry point search", not a "project
+# search" (eg from a modules/packages list)
+# moreover we should differentiate between initial import vs runtime imports,
+# no ?
+
+for _kw in UNAVAILABLE_KEYWORDS:
+ def visit_unavailable_keyword(self, node, name=_kw):
+ if not self._rpython:
+ return
+ self.add_message('E1201', node=node, args=name)
+ setattr(RPythonChecker, 'visit_%s' % _kw, visit_unavailable_keyword)
+del _kw
+
+def register(linter):
+ """required method to auto register this checker """
+ linter.register_checker(RPythonChecker(linter))
diff --git a/checkers/similar.py b/checkers/similar.py
new file mode 100644
index 0000000..0c5fe7b
--- /dev/null
+++ b/checkers/similar.py
@@ -0,0 +1,334 @@
+# pylint: disable-msg=W0622
+# Copyright (c) 2004-2006 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.,
+# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+"""a similarties / code duplication command line tool and pylint checker
+"""
+from __future__ import generators
+
+__revision__ = '$Id: similar.py,v 1.14 2006-03-29 08:24:32 syt Exp $'
+
+import sys
+
+from logilab.common.compat import set, izip, sum, enumerate
+from logilab.common.ureports import Table
+
+from pylint.interfaces import IRawChecker
+from pylint.checkers import BaseChecker, table_lines_from_stats
+
+
+class Similar:
+ """finds copy-pasted lines of code in a project"""
+
+ def __init__(self, min_lines=4, ignore_comments=False,
+ ignore_docstrings=False):
+ self.min_lines = min_lines
+ self.ignore_comments = ignore_comments
+ self.ignore_docstrings = ignore_docstrings
+ self.linesets = []
+
+ def append_stream(self, streamid, stream):
+ """append a file to search for similarities"""
+ self.linesets.append(LineSet(streamid,
+ stream.readlines(),
+ self.ignore_comments,
+ self.ignore_docstrings))
+
+ 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 = {}
+ for num, lineset1, idx1, lineset2, idx2 in self._iter_sims():
+ duplicate = no_duplicates.setdefault(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 no_duplicates.iteritems():
+ 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 = list(couples)
+ couples.sort()
+ for lineset, idx in couples:
+ print "==%s:%s" % (lineset.name, idx)
+ # pylint: disable-msg=W0631
+ for line in lineset._real_lines[idx:idx+num]:
+ print " ", line,
+ 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=%s" \
+ % (nb_total_lignes, nb_lignes_dupliquees,
+ nb_lignes_dupliquees*1. / 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(
+ izip(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):
+ 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 = ''
+ # XXX cut when a line begins with code but end with a comment
+ if ignore_comments and line.startswith('#'):
+ line = ''
+ strippedlines.append(line)
+ return strippedlines
+
+class LineSet:
+ """Holds and indexes all the lines of a single source file"""
+ def __init__(self, name, lines, ignore_comments=False,
+ ignore_docstrings=False):
+ self.name = name
+ self._real_lines = lines
+ self._stripped_lines = stripped_lines(lines, ignore_comments,
+ ignore_docstrings)
+ self._index = self._mk_index()
+
+ def __str__(self):
+ return '<Lineset for %s>' % self.name
+
+ def __len__(self):
+ return len(self._real_lines)
+
+ def __getitem__(self, index):
+ return self._stripped_lines[index]
+
+ def __cmp__(self, other):
+ return cmp(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 = {}
+ for line_no, line in enumerate(self._stripped_lines):
+ if line:
+ index.setdefault(line, []).append( line_no )
+ return index
+
+
+MSGS = {'R0801': ('Similar lines in %s files\n%s',
+ '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 experiments 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' : '<int>',
+ 'help' : 'Minimum lines number of a similarity.'}),
+ ('ignore-comments',
+ {'default' : True, 'type' : 'yn', 'metavar' : '<y or n>',
+ 'help': 'Ignore comments when computing similarities.'}
+ ),
+ ('ignore-docstrings',
+ {'default' : True, 'type' : 'yn', 'metavar' : '<y or n>',
+ 'help': 'Ignore docstrings when computing similarities.'}
+ ),
+ )
+ # reports
+ reports = ( ('R0801', '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, opt_name, value, action=None, opt_dict=None):
+ """method called to set an option (registered in the options list)
+
+ overridden to report options setting to Similar
+ """
+ BaseChecker.set_option(self, opt_name, value, action, opt_dict)
+ if opt_name == 'min-similarity-lines':
+ self.min_lines = self.config.min_similarity_lines
+ elif opt_name == 'ignore-comments':
+ self.ignore_comments = self.config.ignore_comments
+ elif opt_name == 'ignore-docstrings':
+ self.ignore_docstrings = self.config.ignore_docstrings
+
+ 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, stream):
+ """process a module
+
+ the module's content is accessible via the stream object
+
+ stream must implements the readlines method
+ """
+ self.append_stream(self.linter.current_name, stream)
+
+ 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-msg=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: similar [-d|--duplicates min_duplicated_lines] \
+[--ignore-comments] file1...'
+ sys.exit(status)
+
+def run(argv=None):
+ """standalone command line access point"""
+ argv = argv or sys.argv[1:]
+ from getopt import getopt
+ s_opts = 'hd:'
+ l_opts = ('help', 'duplicates=', 'ignore-comments')
+ min_lines = 4
+ ignore_comments = 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 == '--ignore-comments':
+ ignore_comments = True
+ if not args:
+ usage(1)
+ sim = Similar(min_lines, ignore_comments)
+ for filename in args:
+ sim.append_stream(filename, open(filename))
+ sim.run()
+
+if __name__ == '__main__':
+ run()
diff --git a/checkers/typecheck.py b/checkers/typecheck.py
new file mode 100644
index 0000000..cbcbddd
--- /dev/null
+++ b/checkers/typecheck.py
@@ -0,0 +1,202 @@
+# Copyright (c) 2006 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.,
+# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+"""try to find more bugs in the code using astng inference capabilities
+"""
+
+from logilab.common.compat import set
+from logilab import astng
+
+from pylint.interfaces import IASTNGChecker
+from pylint.checkers import BaseChecker
+from pylint.checkers.utils import safe_infer, is_super, display_type
+
+MSGS = {
+ 'E1101': ('%s %r has no %r member',
+ 'Used when a variable is accessed for an unexistant member.'),
+ 'E1102': ('%s is not callable',
+ 'Used when an object being called has been infered to a non \
+ callable object'),
+ 'E1103': ('%s %r has no %r member (but some types could not be inferred)',
+ 'Used when a variable is accessed for an unexistant member, but \
+ astng was not able to interpret all possible types of this \
+ variable.'),
+ 'E1111': ('Assigning to function call which doesn\'t return',
+ 'Used when an assigment is done on a function call but the \
+ infered function doesn\'t return anything.'),
+ 'W1111': ('Assigning to function call which only returns None',
+ 'Used when an assigment is done on a function call but the \
+ infered function returns nothing but None.'),
+ }
+
+class TypeChecker(BaseChecker):
+ """try to find bugs in the code using type inference
+ """
+
+ __implements__ = (IASTNGChecker,)
+
+ # configuration section name
+ name = 'typecheck'
+ # messages
+ msgs = MSGS
+ priority = -1
+ # configuration options
+ options = (('ignore-mixin-members',
+ {'default' : True, 'type' : 'yn', 'metavar': '<y_or_n>',
+ 'help' : 'Tells wether missing members accessed in mixin \
+class should be ignored. A mixin class is detected if its name ends with \
+"mixin" (case insensitive).'}
+ ),
+
+ ('ignored-classes',
+ {'default' : ('SQLObject',),
+ 'type' : 'csv',
+ 'metavar' : '<members names>',
+ 'help' : 'List of classes names for which member attributes \
+should not be checked (useful for classes with attributes dynamicaly set).'}
+ ),
+
+ ('zope',
+ {'default' : False, 'type' : 'yn', 'metavar': '<y_or_n>',
+ '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' : 'csv',
+ 'metavar' : '<members names>',
+ 'help' : 'List of members which are set dynamically and \
+missed by pylint inference system, and so shouldn\'t trigger E0201 when \
+accessed.'}
+ ),
+ )
+ def __init__(self, linter=None):
+ BaseChecker.__init__(self, linter)
+ self.generated_members = list(self.config.generated_members)
+ if self.config.zope:
+ self.generated_members.extend(('REQUEST', 'acl_users', 'aq_parent'))
+
+ 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 infered nodes has the accessed attribute.
+
+ function/method, super call and metaclasses are ignored
+ """
+ if node.attrname in self.config.generated_members:
+ # attribute is marked as generated, stop here
+ return
+ try:
+ infered = list(node.expr.infer())
+ except astng.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 astng.YES:
+ inference_failure = True
+ continue
+ # skip None anyway
+ if isinstance(owner, astng.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:
+ owner.getattr(node.attrname)
+ except AttributeError:
+ # XXX method / function
+ continue
+ except astng.NotFoundError, ex:
+ if isinstance(owner, astng.Instance) \
+ and owner.has_dynamic_getattr():
+ continue
+ # explicit skipping of optparse'Values class
+ if owner.name == 'Values' and \
+ owner.root().name in ('optik', 'optparse'):
+ continue
+ 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, astng.Instance):
+ actual = owner._proxied
+ else:
+ actual = owner
+ if actual in done:
+ continue
+ done.add(actual)
+ if inference_failure:
+ msgid = 'E1103'
+ else:
+ msgid = 'E1101'
+ self.add_message(msgid, node=node,
+ args=(display_type(owner), name,
+ node.attrname))
+
+
+ def visit_assign(self, node):
+ """check that if assigning to a function call, the function is
+ possibly returning something valuable
+ """
+ if not isinstance(node.expr, astng.CallFunc):
+ return
+ function_node = safe_infer(node.expr.node)
+ # skip class, generator and uncomplete function definition
+ if not (isinstance(function_node, astng.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(astng.Return,
+ skip_klass=astng.Function))
+ if len(returns) == 0:
+ self.add_message('E1111', node=node)
+ else:
+ for rnode in returns:
+ if not (isinstance(rnode.value, astng.Name)
+ and rnode.value.name == 'None'):
+ break
+ else:
+ self.add_message('W1111', node=node)
+
+ def visit_callfunc(self, node):
+ """check that called method are infered to callable objects
+ """
+ called = safe_infer(node.node)
+ # only function, generator and object defining __call__ are allowed
+ if called is not None and not called.callable():
+ self.add_message('E1102', node=node, args=node.node.as_string())
+
+
+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
new file mode 100644
index 0000000..575a715
--- /dev/null
+++ b/checkers/utils.py
@@ -0,0 +1,181 @@
+# pylint: disable-msg=W0611
+#
+# Copyright (c) 2003-2007 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.,
+# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+"""some functions that may be usefull for various checkers
+"""
+
+from logilab.common import flatten
+
+from logilab import astng
+from logilab.astng.utils import are_exclusive
+try:
+ # python >= 2.4
+ COMP_NODE_TYPES = (astng.ListComp, astng.GenExpr)
+ FOR_NODE_TYPES = (astng.For, astng.ListCompFor, astng.GenExprFor)
+except AttributeError:
+ COMP_NODE_TYPES = astng.ListComp
+ FOR_NODE_TYPES = (astng.For, astng.ListCompFor)
+
+def safe_infer(node):
+ """return the infered value for the given node.
+ Return None if inference failed or if there is some ambiguity (more than
+ one node has been infered)
+ """
+ try:
+ inferit = node.infer()
+ value = inferit.next()
+ except astng.InferenceError:
+ return
+ try:
+ inferit.next()
+ return # None if there is ambiguity on the infered node
+ 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 == '__builtin__':
+ return True
+ return False
+
+def is_error(node):
+ """return true if the function does nothing but raising an exception"""
+ for child_node in node.code.getChildNodes():
+ if isinstance(child_node, astng.Raise):
+ return True
+ return False
+
+def is_raising(stmt):
+ """return true if the given statement node raise an exception
+ """
+ for node in stmt.nodes:
+ if isinstance(node, astng.Raise):
+ return True
+ return False
+
+def is_empty(node):
+ """return true if the given node does nothing but 'pass'"""
+ children = node.getChildNodes()
+ return len(children) == 1 and isinstance(children[0], astng.Pass)
+
+builtins = __builtins__.copy()
+SPECIAL_BUILTINS = ('__builtins__',) # '__path__', '__file__')
+
+def is_builtin(name): # was is_native_builtin
+ """return true if <name> could be considered as a builtin defined by python
+ """
+ if builtins.has_key(name):
+ return True
+ if name in SPECIAL_BUILTINS:
+ return True
+ return False
+
+def is_defined_before(var_node, comp_node_types=COMP_NODE_TYPES):
+ """return True if the variable node is defined by a parent node (list
+ or generator comprehension, lambda) or in a previous sibling node
+ one 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(astng.AssName):
+ if ass_node.name == varname:
+ return True
+ elif isinstance(_node, astng.For):
+ for ass_node in _node.assign.nodes_of_class(astng.AssName):
+ if ass_node.name == varname:
+ return True
+ elif isinstance(_node, (astng.Lambda, astng.Function)):
+ if varname in flatten(_node.argnames):
+ return True
+ if getattr(_node, 'name', None) == varname:
+ return True
+ break
+ _node = _node.parent
+ # possibly multiple statements on the same line using semi colon separator
+ stmt = var_node.statement()
+ _node = stmt.previous_sibling()
+ lineno = stmt.lineno
+ while _node and _node.lineno == lineno:
+ for ass_node in _node.nodes_of_class(astng.AssName):
+ if ass_node.name == varname:
+ return True
+ for imp_node in _node.nodes_of_class( (astng.From, astng.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 name is used in function default argument's value
+ """
+ parent = node.parent
+ if parent is None:
+ return 0
+ if isinstance(parent, astng.Function) and parent.defaults and \
+ node in parent.defaults:
+ return 1
+ return is_func_default(parent)
+
+def is_ancestor_name(frame, node):
+ """return True if `frame` is a astng.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(astng.Name):
+ return True
+ return False
+
+def assign_parent(node):
+ """return the higher parent which is not an AssName, AssTuple or AssList
+ node
+ """
+ while node and isinstance(node, (astng.AssName,
+ astng.AssTuple,
+ astng.AssList)):
+ 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], astng.Function) and \
+ ancestor[name].is_abstract(pass_is_abstract=False):
+ return True
+ return False
+
+def overrides_a_method(class_node, name):
+ """return True if <name> is a method overridden from an ancestor"""
+ for ancestor in class_node.ancestors():
+ if name in ancestor and isinstance(ancestor[name], astng.Function):
+ return True
+ return False
+
+def display_type(node):
+ """return the type of this node for screen display"""
+ if isinstance(node, astng.Instance):
+ return 'Instance of'
+ elif isinstance(node, astng.Module):
+ return 'Module'
+ return 'Class'
diff --git a/checkers/variables.py b/checkers/variables.py
new file mode 100644
index 0000000..71fb0eb
--- /dev/null
+++ b/checkers/variables.py
@@ -0,0 +1,444 @@
+# Copyright (c) 2003-2007 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.,
+# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+"""variables checkers for Python code
+"""
+
+from copy import copy
+
+from logilab.common.compat import enumerate
+from logilab import astng
+from logilab.astng.lookup import builtin_lookup
+
+from pylint.interfaces import IASTNGChecker
+from pylint.checkers import BaseChecker
+from pylint.checkers.utils import is_error, is_builtin, is_func_default, \
+ is_ancestor_name, assign_parent, are_exclusive, \
+ is_defined_before #, is_parent, FOR_NODE_TYPES
+
+
+
+MSGS = {
+ 'E0601': ('Using variable %r before assignment',
+ 'Used when a local variable is accessed before it\'s \
+ assignment.'),
+ 'E0602': ('Undefined variable %r',
+ 'Used when an undefined variable is accessed.'),
+
+ 'E0611': ('No name %r in module %r',
+ 'Used when a name cannot be found in a module.'),
+
+ 'W0601': ('Global variable %r undefined at the module level',
+ '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 assigment is done',
+ 'Used when a variable is defined through the "global" statement \
+ but no assigment to this variable is done.'),
+ 'W0603': ('Using the global statement', # W0121
+ '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
+ 'Used when you use the "global" statement at the module level \
+ since it has no effect'),
+ 'W0611': ('Unused import %s',
+ 'Used when an imported module or variable is not used.'),
+ 'W0612': ('Unused variable %r',
+ 'Used when a variable is defined but not used.'),
+ 'W0613': ('Unused argument %r',
+ 'Used when a function or method argument is not used.'),
+ 'W0614': ('Unused import %s from 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)',
+ 'Used when a variable\'s name hide a name defined in the outer \
+ scope.'),
+ 'W0622': ('Redefining built-in %r',
+ 'Used when a variable or function override a built-in.'),
+
+ 'W0631': ('Using possibly undefined loop variable %r',
+ '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.'),
+ }
+
+class VariablesChecker(BaseChecker):
+ """checks for
+ * unused variables / imports
+ * undefined variables
+ * redefinition of variable from builtins or from an outer scope
+ * use of variable before assigment
+ """
+
+ __implements__ = IASTNGChecker
+
+ name = 'variables'
+ msgs = MSGS
+ priority = -1
+ options = (
+ ("init-import",
+ {'default': 0, 'type' : 'yn', 'metavar' : '<y_or_n>',
+ 'help' : 'Tells wether we should check for unused import in \
+__init__ files.'}),
+ ("dummy-variables-rgx",
+ {'default': ('_|dummy'),
+ 'type' :'regexp', 'metavar' : '<regexp>',
+ 'help' : 'A regular expression matching names used \
+ for dummy variables (i.e. not used).'}),
+ ("additional-builtins",
+ {'default': (), 'type' : 'csv',
+ 'metavar' : '<comma separated list>',
+ 'help' : 'List of additional names supposed to be defined in \
+builtins. Remember that you should avoid to define new builtins when possible.'
+ }),
+ )
+ def __init__(self, linter=None):
+ BaseChecker.__init__(self, linter)
+ self._to_consume = None
+ self._checking_mod_attr = None
+ self._vars = 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')]
+ self._vars = []
+ for name, stmts in node.locals.items():
+ if name in ('__name__', '__doc__', '__file__', '__path__') \
+ and len(stmts) == 1:
+ # only the definition added by the astng builder, continue
+ continue
+ if self._is_builtin(name):
+ self.add_message('W0622', args=name, node=stmts[0])
+
+ def leave_module(self, node):
+ """leave module: check globals
+ """
+ assert len(self._to_consume) == 1
+ not_consumed = self._to_consume.pop()[0]
+ # don't check unused imports in __init__ files
+ if not self.config.init_import and node.package:
+ return
+ for name, stmts in not_consumed.items():
+ stmt = stmts[0]
+ if isinstance(stmt, astng.Import):
+ self.add_message('W0611', args=name, node=stmt)
+ elif isinstance(stmt, astng.From) and stmt.modname != '__future__':
+ if stmt.names[0][0] == '*':
+ self.add_message('W0614', args=name, node=stmt)
+ else:
+ self.add_message('W0611', args=name, node=stmt)
+ del self._to_consume
+ del self._vars
+
+ 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), {}, 'genexpr'))
+
+ def leave_genexpr(self, _):
+ """leave genexpr: 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
+ """
+ globs = node.root().globals
+ for name, stmt in node.items():
+ if globs.has_key(name) and not isinstance(stmt, astng.Global):
+ line = globs[name][0].lineno
+ self.add_message('W0621', args=(name, line), node=stmt)
+ elif self._is_builtin(name):
+ self.add_message('W0622', args=name, node=stmt)
+ self._to_consume.append((copy(node.locals), {}, 'function'))
+ self._vars.append({})
+
+ def leave_function(self, node):
+ """leave function: check function's locals are consumed
+ """
+ not_consumed = self._to_consume.pop()[0]
+ self._vars.pop(0)
+ is_method = node.is_method()
+ klass = node.parent.frame()
+ # don't check arguments of abstract methods or within an interface
+ if is_method and (klass.type == 'interface' or node.is_abstract()):
+ return
+ if is_error(node):
+ return
+ authorized_rgx = self.config.dummy_variables_rgx
+ for name, stmts in not_consumed.items():
+ # 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, astng.Global):
+ continue
+ # care about functions with unknown argument (builtins)
+ if node.argnames is not None and name in node.argnames:
+ # don't warn if the first argument of a method is not used
+ if is_method and node.argnames and name == node.argnames[0]:
+ continue
+ # don't check callback arguments
+ if node.name.startswith('cb_') or \
+ node.name.endswith('_cb'):
+ continue
+ self.add_message('W0613', args=name, node=node)
+ else:
+ self.add_message('W0612', args=name, node=stmt)
+
+ def visit_global(self, node):
+ """check names imported exists in the global scope"""
+ frame = node.frame()
+ if isinstance(frame, astng.Module):
+ self.add_message('W0604', node=node)
+ return
+ module = frame.root()
+ default_message = True
+ for name in node.names:
+ try:
+ assign_nodes = module.getattr(name)
+ except astng.NotFoundError:
+ # unassigned global, skip
+ assign_nodes = []
+ for anode in assign_nodes:
+ if anode.frame() is frame:
+ # same scope level assigment
+ break
+ else:
+ # global but no assigment
+ self.add_message('W0602', args=name, node=node)
+ default_message = False
+ if not assign_nodes:
+ continue
+ for anode in assign_nodes:
+ if anode.frame() is module:
+ # module level assigment
+ break
+ else:
+ # global undefined at the module scope
+ self.add_message('W0601', args=name, node=node)
+ default_message = False
+ if default_message:
+ self.add_message('W0603', node=node)
+
+ def _loopvar_name(self, node, name):
+ # filter variables according to node's scope
+ astmts = [stmt for stmt in node.lookup(name)[1]
+ if hasattr(stmt, 'ass_type') and
+ not stmt.statement().parent_of(node)]
+ # filter variables according their respective scope
+ if not astmts or 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):
+ continue
+ _astmts.append(stmt)
+ astmts = _astmts
+ if len(astmts) == 1:
+ ass = astmts[0].ass_type()
+ if isinstance(ass, (astng.For, astng.ListCompFor, astng.GenExpr)) \
+ and not ass.statement() is node.statement():
+ self.add_message('W0631', args=name, node=node)
+
+ def visit_name(self, node):
+ """check that a name is defined if the current scope and doesn't
+ redefine a built-in
+ """
+ name = node.name
+ stmt = node.statement()
+ frame = stmt.scope()
+ # if the name node is used as a function default argument's value, then
+ # start from the parent frame of the function instead of the function
+ # frame
+ if is_func_default(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
+ 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
+ if scope_type == 'class' and i != start_index:
+ continue
+ # the name has already been consumed, only check it's not a loop
+ # variable used outside the loop
+ if consumed.has_key(name):
+ self._loopvar_name(node, name)
+ break
+ # mark the name as consumed if it's defined in this scope
+ # (ie no KeyError is raise by "to_consume[name]"
+ try:
+ consumed[name] = to_consume[name]
+ # checks for use before assigment
+ # FIXME: the last condition should just check attribute access
+ # is protected by a try: except NameError: (similar to #9219)
+ defnode = assign_parent(to_consume[name][0])
+ if defnode is not None:
+ defstmt = defnode.statement()
+ defframe = defstmt.frame()
+ maybee0601 = True
+ if not frame is defframe:
+ maybee0601 = False
+ elif defframe.parent is None:
+ # we are at the module level, check the name is not
+ # defined in builtins
+ if 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
+ if (maybee0601
+ and stmt.source_line() <= defstmt.source_line()
+ and not is_defined_before(node)
+ and not are_exclusive(stmt, defstmt)):
+ self.add_message('E0601', args=name, node=node)
+ del to_consume[name]
+ # check it's not a loop variable used outside the loop
+ self._loopvar_name(node, name)
+ break
+ except KeyError:
+ continue
+ else:
+ # we have not found the name, if it isn't a builtin, that's an
+ # undefined name !
+ if not self._is_builtin(name):
+ self.add_message('E0602', args=name, node=node)
+
+ def visit_import(self, node):
+ """check modules attribute accesses"""
+ for name, _ in node.names:
+ parts = name.split('.')
+ try:
+ module = node.infer_name_module(parts[0]).next()
+ except astng.ResolveError:
+ continue
+ self._check_module_attrs(node, module, parts[1:])
+
+ def visit_from(self, node):
+ """check modules attribute accesses"""
+ name_parts = node.modname.split('.')
+ try:
+ module = node.root().import_module(name_parts[0])
+ except KeyboardInterrupt:
+ raise
+ 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('.'))
+
+## def leave_getattr(self, node):
+## """check modules attribute accesses
+
+## this function is a "leave_" because when parsing 'a.b.c'
+## we want to check the innermost expression first.
+## """
+## if isinstance(node.expr, astng.Name):
+## try:
+## module = node.expr.infer().next()
+## except astng.InferenceError:
+## return
+## if not isinstance(module, astng.Module):
+## # Not a module, don't check
+## return
+## elif self._checking_mod_attr is not None:
+## module = self._checking_mod_attr
+## else:
+## return
+## self._checking_mod_attr = self._check_module_attrs(node, module,
+## [node.attrname])
+
+## def leave_default(self, node):
+## """by default, reset the _checking_mod_attr attribute"""
+## self._checking_mod_attr = None
+
+ 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, astng.Module), module
+ while module_names:
+ name = module_names.pop(0)
+ if name == '__dict__':
+ module = None
+ break
+ try:
+ module = module.getattr(name)[0].infer().next()
+ except astng.NotFoundError:
+ self.add_message('E0611', args=(name, module.name), node=node)
+ return None
+ except astng.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('E0611', node=node,
+ args=('.'.join(module_names), modname))
+ return None
+ if isinstance(module, astng.Module):
+ return module
+ return None
+
+ def _is_builtin(self, name):
+ """return True if the name is defined in the native builtin or
+ in the user specific builtins
+ """
+ return is_builtin(name) or name in self.config.additional_builtins
+
+
+def register(linter):
+ """required method to auto register this checker"""
+ linter.register_checker(VariablesChecker(linter))
diff --git a/config.py b/config.py
new file mode 100644
index 0000000..84bd893
--- /dev/null
+++ b/config.py
@@ -0,0 +1,148 @@
+# 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.,
+# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+""" Copyright (c) 2003-2006 LOGILAB S.A. (Paris, FRANCE).
+ http://www.logilab.fr/ -- mailto:contact@logilab.fr
+
+ utilities for PyLint configuration :
+ _ pylintrc
+ _ pylint.d (PYLINT_HOME)
+"""
+
+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 os.environ.has_key('PYLINTHOME'):
+ 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')
+
+if not exists(PYLINT_HOME):
+ try:
+ os.mkdir(PYLINT_HOME)
+ except OSError:
+ print >> sys.stderr, 'Unable to create directory %s' % PYLINT_HOME
+
+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:
+ return pickle.load(open(data_file))
+ except:
+ return {}
+
+def save_results(results, base):
+ """pickle results"""
+ data_file = get_pdata_path(base, 1)
+ try:
+ pickle.dump(results, open(data_file, 'w'))
+ except OSError:
+ print >> sys.stderr, 'Unable to create file %s' % data_file
+
+# 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 os.environ.has_key('PYLINTRC') and exists(os.environ['PYLINTRC']):
+ pylintrc = os.environ['PYLINTRC']
+ else:
+ if USER_HOME == '~' or USER_HOME == '/root':
+ pylintrc = ".pylintrc"
+ else:
+ pylintrc = join(USER_HOME, '.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 data of persistent run will be stored. If not
+found, it defaults to ~/.pylint.d/ or .pylint.d (in the current working
+directory) . The current PYLINTHOME is %(PYLINT_HOME)s.
+ * PYLINTRC
+ path to the configuration file. If not found, it will use the first
+existant file in ~/.pylintrc, /etc/pylintrc. The current PYLINTRC is
+%(PYLINTRC)s.
+''' % 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/debian/NEWS.Debian b/debian/NEWS.Debian
new file mode 100644
index 0000000..0a7e3eb
--- /dev/null
+++ b/debian/NEWS.Debian
@@ -0,0 +1,10 @@
+pylint (0.13.2-2) unstable; urgency=low
+
+ * pylint.el is no longer installed for emacs, because it is not
+ compatible with emacs22 and has a large number of related bugs. It
+ will be reenabled in a future version, when all the bugs have been
+ dealt with by upstream (patches are welcome by the way). Emacs21
+ users may get it from /usr/share/doc/pylint/examples/pylint.el
+
+ -- Alexandre Fayolle <afayolle@debian.org> Wed, 04 Jul 2007 12:18:23 +0200
+
diff --git a/debian/changelog b/debian/changelog
new file mode 100644
index 0000000..99d4b45
--- /dev/null
+++ b/debian/changelog
@@ -0,0 +1,318 @@
+pylint (0.14.0-3) unstable; urgency=low
+
+ * Acknowledge NMU: thanks a lot, your patches have been backported in
+ upstream code (closes: #427244, #448102, #415485, #431653)
+
+ --
+
+pylint (0.14.0-2.1) unstable; urgency=low
+
+ * Non-maintainer upload.
+ * BF: flavor -> debian-emacs-flavor in startup
+ * NF: added pylint-options variable visible to users to tune up (closes:
+ #427244)
+ * BF: reenabled installation of pylint.el (closes: #448102)
+ * Recent upstream of pylint.el fixed few bugs which weren't closed
+ in the upload to Debian. Since this is first upload which enables
+ pylint.el, imho it is ok to close them here (closes: #415485, #431653)
+
+ -- Yaroslav Halchenko <debian@onerussian.com> Mon, 03 Mar 2008 22:22:07 -0500
+
+pylint (0.14.0-2) unstable; urgency=low
+
+ * Acknowledge NMU by Kumar Appaiah <akumar@ee.iitm.ac.in> (Closes: #454401)
+ * Debian upload of the new upstream release
+ * Install modules for all available python versions (Closes: #438438)
+ * new standards version, no changes required
+
+ -- Alexandre Fayolle <afayolle@debian.org> Wed, 13 Feb 2008 17:36:30 +0100
+
+pylint (0.14.0-1) unstable; urgency=low
+
+ * new upstream release
+
+ -- Sylvain Thénault <Sylvain.Thenault@logilab.fr> Mon, 14 Jan 2008 13:34:24 +0100
+
+pylint (0.13.2-2.1) unstable; urgency=low
+
+ * Non-maintainer upload.
+ * debian/pylint.postrm:
+ + Added to remove old Emacs mode upon purge.
+ (Closes: #454401)
+
+ -- Kumar Appaiah <akumar@ee.iitm.ac.in> Thu, 31 Jan 2008 22:27:18 +0530
+
+pylint (0.13.2-2) unstable; urgency=low
+
+ * Upload package to debian (closes: #426418)
+ * tests are no longer installed in /usr/share/doc/pylint, use the source
+ package to get them
+ * pylint.el is no longer installed for emacs, because it is not
+ compatible with emacs22. It will be reenabled in a future version,
+ when all the bugs have been dealt with by upstream. Emacs21 users may
+ get it from /usr/share/doc/pylint/examples/pylint.el. Added a note
+ about this in NEWS.Debian.
+ * Only Recommend python-tk and give a nicer warning in the pylint-gui
+ script if tkinter is not available.
+ * Updated versions of dependencies on logilab-common and astng
+
+ -- Alexandre Fayolle <afayolle@debian.org> Wed, 04 Jul 2007 12:21:24 +0200
+
+pylint (0.13.2-1) unstable; urgency=low
+
+ * new upstream release
+
+ -- Sylvain Thénault <sylvain.thenault@logilab.fr> Thu, 07 Jun 2007 16:44:53 +0200
+
+pylint (0.13.1-1) unstable; urgency=low
+
+ * new upstream release
+
+ -- Sylvain Thénault <sylvain.thenault@logilab.fr> Fri, 2 Mar 2007 08:24:08 +0100
+
+pylint (0.13.0-1) unstable; urgency=low
+
+ * new upstream release
+
+ -- Sylvain Thénault <sylvain.thenault@logilab.fr> Thu, 22 Feb 2007 11:28:23 +0100
+
+pylint (0.12.2-1) unstable; urgency=low
+
+ * new upstream release
+
+ -- Sylvain Thénault <sylvain.thenault@logilab.fr> Thu, 23 Nov 2006 16:08:28 +0100
+
+pylint (0.12.1-1) unstable; urgency=low
+
+ * new uptream release
+
+ -- Sylvain Thénault <sylvain.thenault@logilab.fr> Mon, 25 Sep 2006 16:46:40 +0200
+
+pylint (0.12.0-2) unstable; urgency=low
+
+ * Upload to Debian
+ * Fixed the XS-Python-Version value (closes: #388158)
+
+ -- Alexandre Fayolle <afayolle@debian.org> Tue, 19 Sep 2006 09:51:03 +0200
+
+pylint (0.12.0-1) unstable; urgency=low
+
+ * new upstream release
+
+ -- Adrien.DiMascio <Adrien.DiMascio@logilab.fr> Thu, 10 Aug 2006 11:18:50 +0200
+
+pylint (0.11.0-2) unstable; urgency=low
+
+ * Updated standards to 3.7.2
+ * use debhelper 5
+ * new python policy
+
+ -- Alexandre Fayolle <afayolle@debian.org> Thu, 15 Jun 2006 10:38:32 +0200
+
+pylint (0.11.0-1) unstable; urgency=low
+
+ * new upstream release, depending on python-astng 0.16
+ * Applied Sebastian Rittau's patch to avoid NameError on InferenceError
+ exception (closes: #358194)
+
+ -- Sylvain Thénault <sylvain.thenault@logilab.fr> Wed, 19 Apr 2006 18:10:47 +0200
+
+pylint (0.10.0-1) unstable; urgency=low
+
+ * new upstream release, depending on python-astng 0.15
+
+ -- Sylvain Thénault <sylvain.thenault@logilab.fr> Mon, 6 Mar 2006 09:43:19 +0100
+
+pylint (0.9.0-3) unstable; urgency=low
+
+ * Added missing provides/replaces/conflicts on pylint-test (closes: #352316)
+
+ -- Alexandre Fayolle <afayolle@debian.org> Mon, 13 Feb 2006 10:07:26 +0100
+
+pylint (0.9.0-2) unstable; urgency=low
+
+ * Build a single package which installs modules in /usr/lib/site-python
+ (closes: #351130)
+ * Remove duplication from man page (closes: #349689)
+ * Fixed typo in control file
+ * upload new release to Debian
+
+ -- Alexandre Fayolle <afayolle@debian.org> Fri, 10 Feb 2006 16:03:37 +0100
+
+pylint (0.9.0-1) unstable; urgency=low
+
+ * fix false positive with staticmethod used on a metaclass (closes: #341121)
+ * reorganization to install into site-python, removing the need for
+ pythonX.X- packages and for the pylint-common and pylint-test packages
+
+ -- Sylvain Thénault <sylvain.thenault@logilab.fr> Tue, 10 Jan 2006 14:19:57 +0100
+
+pylint (0.8.1-1) unstable; urgency=low
+
+ * added missing dependancy to logilab-astng
+ * added missing .docs and .examples files
+ * update control'standards-version to 3.6.2
+ * fixed FSF address in the copyright file
+ * new upstream release
+
+ -- Sylvain Thénault <sylvain.thenault@logilab.fr> Mon, 7 Nov 2005 15:40:52 +0100
+
+pylint (0.8.0-1) unstable; urgency=low
+
+ * new upstream release
+
+ -- Sylvain Thénault <sylvain.thenault@logilab.fr> Fri, 21 Oct 2005 18:44:24 +0200
+
+pylint (0.7.0-1) unstable; urgency=low
+
+ * new upstream release (closes: #310957)
+
+ -- Sylvain Thénault <sylvain.thenault@logilab.fr> Fri, 27 May 2005 11:17:44 +0200
+
+pylint (0.6.4-1) unstable; urgency=low
+
+ * new upstream release
+ * added man page for pylint
+
+ -- Sylvain Thénault <sylvain.thenault@logilab.fr> Thu, 14 Apr 2005 12:02:15 +0200
+
+pylint (0.6.3-1) unstable; urgency=low
+
+ * new upstream release
+
+ -- Sylvain Thénault <sylvain.thenault@logilab.fr> Thu, 24 Feb 2005 17:44:35 +0100
+
+pylint (0.6.2-1) unstable; urgency=low
+
+ * new upstream release
+
+ -- Sylvain Thénault <sylvain.thenault@logilab.fr> Wed, 16 Feb 2005 12:00:47 +0100
+
+pylint (0.6.1-1) unstable; urgency=low
+
+ * new upstream release
+ * added option to specify rc file location (closes: #265159)
+
+ -- Sylvain Thénault <sylvain.thenault@logilab.fr> Fri, 4 Feb 2005 16:48:09 +0100
+
+pylint (0.6.0-1) unstable; urgency=low
+
+ * new upstream release
+ * build package for python 2.4
+ * remove unused directory from logilab-common.dirs
+ * updated copyright
+
+ -- Sylvain Thénault <sylvain.thenault@logilab.fr> Thu, 20 Jan 2005 18:06:29 +0100
+
+pylint (0.5.0-2) unstable; urgency=low
+
+ * Patched pylint.el using latest upsrteam CVS (closes: #280870)
+
+ -- Alexandre Fayolle <afayolle@debian.org> Mon, 15 Nov 2004 10:59:51 +0100
+
+pylint (0.5.0-1) unstable; urgency=low
+
+ * use Build-depends instead of Build-depends-indep in control
+ * new upstream release
+ * updated debian/watch file to version 2
+
+ -- Alexandre Fayolle <afayolle@debian.org> Tue, 9 Nov 2004 16:22:47 +0100
+
+pylint (0.4.2-2) unstable; urgency=low
+
+ * fixed typos in debian/control (closes: #265156)
+ * updated description of pylint-test
+ * changed dependency on pylint-common to a recommendation (closes: #265157)
+ * updated maintainer address
+
+ -- Alexandre Fayolle <afayolle@debian.org> Sun, 15 Aug 2004 10:39:06 +0200
+
+pylint (0.4.2-1) unstable; urgency=low
+
+ * new upstream release
+ * initial upload to Debian (closes: #258235)
+
+ -- Alexandre Fayolle <alexandre.fayolle@logilab.fr> Thu, 8 Jul 2004 12:54:18 +0200
+
+pylint (0.4.0-1) unstable; urgency=low
+
+ * new upstream release
+
+ -- Sylvain Thénault <sylvain.thenault@logilab.fr> Mon, 10 May 2004 17:03:04 +0200
+
+pylint (0.3.3-1) unstable; urgency=low
+
+ * new upstream release
+ * emacs lisp for pylint in a new pylint-common package
+
+ -- Sylvain Thénault <sylvain.thenault@logilab.fr> Mon, 16 Feb 2004 18:09:23 +0100
+
+pylint (0.3.2-1) unstable; urgency=low
+
+ * new upstream release
+
+ -- Sylvain Thénault <sylvain.thenault@logilab.fr> Tue, 23 Dec 2003 14:56:04 +0100
+
+pylint (0.3.1-1) unstable; urgency=low
+
+ * new upstream release
+
+ -- Sylvain Thénault <sylvain.thenault@logilab.fr> Fri, 5 Dec 2003 16:20:44 +0100
+
+pylint (0.3.0-1) unstable; urgency=low
+
+ * new upstream release
+ * depends on logilab.common >= 0.4
+ * build depends on debhelper >= 4.0
+
+ -- Sylvain Thénault <sylvain.thenault@logilab.fr> Wed, 19 Nov 2003 11:07:45 +0100
+
+pylint (0.2.1-2) unstable; urgency=low
+
+ * fixed dependency on logilab-common (>=0.3.4) since earlier versions
+ caused bugs with some python2.3 code
+ * included sample pylintrc files with the documentation
+ * added documentation that had disappeared in the previous 0.2.1-1
+ * only puts html documentation in doc/html/, all others in doc/
+
+ -- Sylvain Thénault <sylvain.thenault@logilab.fr> Fri, 10 Oct 2003 12:11:53 +0200
+
+pylint (0.2.1-1) unstable; urgency=low
+
+ * new upstream release
+ * package renamed to pylint instead of logilab-pylint
+ * move tests in a separated package
+
+ -- Sylvain Thénault <sylvain.thenault@logilab.fr> Fri, 10 Oct 2003 09:39:22 +0200
+
+logilab-pylint (0.2.0-1) unstable; urgency=low
+
+ * new upstream release
+ * dropped python2.1 support
+
+ -- Sylvain Thénault <sylvain.thenault@logilab.fr> Fri, 12 Sep 2003 18:26:15 +0200
+
+logilab-pylint (0.1.2-1) unstable; urgency=low
+
+ * new upstream release
+
+ -- Sylvain Thénault <sylvain.thenault@logilab.fr> Wed, 11 Jun 2003 15:21:44 +0200
+
+logilab-pylint (0.1.1-2) unstable; urgency=low
+
+ * fix dependencie to logilab.common
+
+ -- Sylvain Thénault <sylvain.thenault@logilab.fr> Wed, 4 Jun 2003 18:07:45 +0200
+
+logilab-pylint (0.1.1-1) unstable; urgency=low
+
+ * New upstream release
+
+ -- Sylvain Thénault <sylvain.thenault@logilab.fr> Mon, 19 May 2003 15:10:25 +0200
+
+logilab-pylint (0.1.0-1) unstable; urgency=low
+
+ * Initial Release.
+
+ -- Sylvain Thénault <sylvain.thenault@logilab.fr> Wed, 23 Apr 2003 14:42:05 +0200
+
diff --git a/debian/compat b/debian/compat
new file mode 100644
index 0000000..7ed6ff8
--- /dev/null
+++ b/debian/compat
@@ -0,0 +1 @@
+5
diff --git a/debian/control b/debian/control
new file mode 100644
index 0000000..76b38b3
--- /dev/null
+++ b/debian/control
@@ -0,0 +1,36 @@
+Source: pylint
+Section: python
+Priority: optional
+Maintainer: Sylvain Thénault <sylvain.thenault@logilab.fr>
+Uploaders: Alexandre Fayolle <afayolle@debian.org>
+Build-Depends: debhelper (>= 5.0.38)
+Build-Depends-Indep: python (>=2.3.5-7), python-central (>=0.5.6)
+Standards-Version: 3.7.3
+Homepage: http://www.logilab.org/Project/name/pylint
+XS-Python-Version: all
+
+Package: pylint
+Architecture: all
+Depends: python, python-logilab-common (>= 0.22.2), python-logilab-astng (>= 0.17.1)
+Recommends: python-tk
+XB-Python-Version: ${python:Versions}
+Conflicts: python2.2-pylint, python2.3-pylint, python2.4-pylint, pylint-common, pylint-test
+Replaces: python2.2-pylint, python2.3-pylint, python2.4-pylint, pylint-common, pylint-test
+Description: python code static checker
+ Pylint is a Python source code analyzer which looks for programming
+ errors, helps enforcing a coding standard and sniffs for some code
+ smells (as defined in Martin Fowler's Refactoring book)
+ .
+ Pylint can be seen as another PyChecker since nearly all tests you
+ can do with PyChecker can also be done with Pylint. However, Pylint
+ offers some more features, like checking length of lines of code,
+ checking if variable names are well-formed according to your coding
+ standard, or checking if declared interfaces are truly implemented,
+ and much more.
+ .
+ Additionally, it is possible to write plugins to add your own checks.
+ .
+ The recommended python-tk package is only for using the pylint-gui
+ script.
+
+
diff --git a/debian/copyright b/debian/copyright
new file mode 100644
index 0000000..e574a13
--- /dev/null
+++ b/debian/copyright
@@ -0,0 +1,29 @@
+This package was debianized by Sylvain Thenault <sylvain.thenault@logilab.fr> Sat, 13 Apr 2002 19:05:23 +0200.
+
+It was downloaded from ftp://ftp.logilab.org/pub/pylint
+
+Upstream Author:
+
+ Sylvain Thenault <sylvain.thenault@logilab.fr>
+
+Copyright:
+
+Copyright (c) 2003-2006 Sylvain Thenault (thenault@gmail.com).
+Copyright (c) 2003-2006 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 St, Fifth Floor, Boston, MA 02110-1301, USA.
+
+On Debian systems, the complete text of the GNU General Public License
+may be found in '/usr/share/common-licenses/GPL'.
diff --git a/debian/pycompat b/debian/pycompat
new file mode 100644
index 0000000..0cfbf08
--- /dev/null
+++ b/debian/pycompat
@@ -0,0 +1 @@
+2
diff --git a/debian/pylint.dirs b/debian/pylint.dirs
new file mode 100644
index 0000000..6a39af5
--- /dev/null
+++ b/debian/pylint.dirs
@@ -0,0 +1,2 @@
+usr/share/doc/pylint/test
+usr/share/emacs/site-lisp/pylint
diff --git a/debian/pylint.docs b/debian/pylint.docs
new file mode 100644
index 0000000..27ad787
--- /dev/null
+++ b/debian/pylint.docs
@@ -0,0 +1,6 @@
+doc/features.html
+doc/quickstart.html
+doc/FAQ.html
+doc/FAQ.txt
+doc/quickstart.txt
+doc/features.txt
diff --git a/debian/pylint.emacsen-install b/debian/pylint.emacsen-install
new file mode 100644
index 0000000..66a8480
--- /dev/null
+++ b/debian/pylint.emacsen-install
@@ -0,0 +1,45 @@
+#! /bin/sh -e
+# /usr/lib/emacsen-common/packages/install/#PACKAGE#
+
+# Written by Jim Van Zandt <jrv@vanzandt.mv.com>, borrowing heavily
+# from the install scripts for gettext by Santiago Vila
+# <sanvila@ctv.es> and octave by Dirk Eddelbuettel <edd@debian.org>.
+
+FLAVOR=$1
+PACKAGE=pylint
+
+if [ ${FLAVOR} = emacs ]; then exit 0; fi
+
+echo install/${PACKAGE}: Handling install for emacsen flavor ${FLAVOR}
+
+#FLAVORTEST=`echo $FLAVOR | cut -c-6`
+#if [ ${FLAVORTEST} = xemacs ] ; then
+# SITEFLAG="-no-site-file"
+#else
+# SITEFLAG="--no-site-file"
+#fi
+FLAGS="${SITEFLAG} -q -batch -l path.el -f batch-byte-compile"
+
+ELDIR=/usr/share/emacs/site-lisp/${PACKAGE}
+ELCDIR=/usr/share/${FLAVOR}/site-lisp/${PACKAGE}
+
+# Install-info-altdir does not actually exist.
+# Maybe somebody will write it.
+if test -x /usr/sbin/install-info-altdir; then
+ echo install/${PACKAGE}: install Info links for ${FLAVOR}
+ install-info-altdir --quiet --section "" "" --dirname=${FLAVOR} /usr/info/${PACKAGE}.info.gz
+fi
+
+install -m 755 -d ${ELCDIR}
+cd ${ELDIR}
+FILES=`echo *.el`
+cp ${FILES} ${ELCDIR}
+cd ${ELCDIR}
+
+cat << EOF > path.el
+(setq load-path (cons "." load-path) byte-compile-warnings nil)
+EOF
+${FLAVOR} ${FLAGS} ${FILES}
+rm -f *.el path.el
+
+exit 0
diff --git a/debian/pylint.emacsen-remove b/debian/pylint.emacsen-remove
new file mode 100644
index 0000000..9795dc4
--- /dev/null
+++ b/debian/pylint.emacsen-remove
@@ -0,0 +1,14 @@
+#!/bin/sh -e
+
+FLAVOR=$1
+PACKAGE=pylint
+
+if [ ${FLAVOR} != emacs ]; then
+ if test -x /usr/sbin/install-info-altdir; then
+ echo remove/${PACKAGE}: removing Info links for ${FLAVOR}
+ install-info-altdir --quiet --remove --dirname=${FLAVOR} /usr/info/${PACKAGE}.info.gz
+ fi
+
+ echo remove/${PACKAGE}: purging byte-compiled files for ${FLAVOR}
+ rm -rf /usr/share/${FLAVOR}/site-lisp/${PACKAGE}
+fi
diff --git a/debian/pylint.emacsen-startup b/debian/pylint.emacsen-startup
new file mode 100644
index 0000000..646bd81
--- /dev/null
+++ b/debian/pylint.emacsen-startup
@@ -0,0 +1,17 @@
+;; -*-emacs-lisp-*-
+;;
+;; Emacs startup file for the Debian GNU/Linux pylint package
+;;
+;; Originally contributed by Nils Naumann <naumann@unileoben.ac.at>
+;; Modified by Dirk Eddelbuettel <edd@debian.org>
+;; Adapted for dh-make by Jim Van Zandt <jrv@vanzandt.mv.com>
+
+;; The pylint package follows the Debian/GNU Linux 'emacsen' policy and
+;; byte-compiles its elisp files for each 'emacs flavor' (emacs19,
+;; xemacs19, emacs20, xemacs20...). The compiled code is then
+;; installed in a subdirectory of the respective site-lisp directory.
+;; We have to add this to the load-path:
+(setq load-path (cons (concat "/usr/share/"
+ (symbol-name flavor)
+ "/site-lisp/pylint") load-path))
+(load-library "pylint")
diff --git a/debian/pylint.examples b/debian/pylint.examples
new file mode 100644
index 0000000..bc35a69
--- /dev/null
+++ b/debian/pylint.examples
@@ -0,0 +1,2 @@
+examples/*
+elisp/pylint.el
diff --git a/debian/pylint.manpages b/debian/pylint.manpages
new file mode 100644
index 0000000..12b9a33
--- /dev/null
+++ b/debian/pylint.manpages
@@ -0,0 +1 @@
+man/pylint.1
diff --git a/debian/pylint.postinst b/debian/pylint.postinst
new file mode 100644
index 0000000..c426afd
--- /dev/null
+++ b/debian/pylint.postinst
@@ -0,0 +1,6 @@
+#! /bin/sh -e
+#
+
+#DEBHELPER#
+
+exit 0
diff --git a/debian/pylint.postrm b/debian/pylint.postrm
new file mode 100644
index 0000000..35920aa
--- /dev/null
+++ b/debian/pylint.postrm
@@ -0,0 +1,8 @@
+#! /bin/sh -e
+
+if [ "$1" = "purge" ]; then
+ # remove old emacs file
+ rm -f /etc/emacs/site-start.d/50pylint.el
+fi
+
+#DEBHELPER#
diff --git a/debian/pylint.prerm b/debian/pylint.prerm
new file mode 100644
index 0000000..c426afd
--- /dev/null
+++ b/debian/pylint.prerm
@@ -0,0 +1,6 @@
+#! /bin/sh -e
+#
+
+#DEBHELPER#
+
+exit 0
diff --git a/debian/rules b/debian/rules
new file mode 100755
index 0000000..06c3942
--- /dev/null
+++ b/debian/rules
@@ -0,0 +1,78 @@
+#!/usr/bin/make -f
+# Sample debian/rules that uses debhelper.
+# GNU copyright 1997 to 1999 by Joey Hess.
+#
+# adapted by Logilab for automatic generation by debianize
+# (part of the devtools project, http://www.logilab.org/projects/devtools)
+#
+# Copyright (c) 2003-2005 LOGILAB S.A. (Paris, FRANCE).
+# http://www.logilab.fr/ -- mailto:contact@logilab.fr
+
+# Uncomment this to turn on verbose mode.
+#export DH_VERBOSE=1
+build: build-stamp
+build-stamp:
+ dh_testdir
+ python setup.py -q build
+ touch build-stamp
+
+clean:
+ dh_testdir
+ dh_testroot
+ rm -f build-stamp configure-stamp
+ rm -rf build
+ find . -name "*.pyc" | xargs rm -f
+ rm -f changelog.gz
+ dh_clean
+
+install: build
+ dh_testdir
+ dh_testroot
+ dh_clean -k
+ dh_installdirs
+ python setup.py -q install --no-compile --prefix=debian/pylint/usr/
+
+ rm -rf debian/pylint/usr/lib/python*/site-packages/pylint/test
+ if head -1 debian/pylint/usr/bin/pylint | grep "^#! */usr/bin" | grep "python" >/dev/null ; then \
+ sed -i "s@^#! */usr/bin/env \+python\$$@#!/usr/bin/python@" debian/pylint/usr/bin/pylint; \
+ fi
+ chmod a+x debian/pylint/usr/bin/pylint
+ if head -1 debian/pylint/usr/bin/pylint-gui | grep "^#! */usr/bin" | grep "python" >/dev/null ; then \
+ sed -i "s@^#! */usr/bin/env \+python\$$@#!/usr/bin/python@" debian/pylint/usr/bin/pylint-gui; \
+ fi
+ chmod a+x debian/pylint/usr/bin/pylint-gui
+ if head -1 debian/pylint/usr/bin/symilar | grep "^#! */usr/bin" | grep "python" >/dev/null ; then \
+ sed -i "s@^#! */usr/bin/env \+python\$$@#!/usr/bin/python@" debian/pylint/usr/bin/symilar; \
+ fi
+ chmod a+x debian/pylint/usr/bin/symilar
+ install -m 644 elisp/pylint.el debian/pylint/usr/share/emacs/site-lisp/pylint/
+
+ # install tests
+ #(cd test && find . -type f -not \( -path '*/CVS/*' -or -name '*.pyc' \) -exec install -D --mode=644 {} ../debian/pylint/usr/share/doc/pylint/test/{} \;)
+
+
+# Build architecture-independent files here.
+binary-indep: build install
+ dh_testdir
+ dh_testroot
+ dh_install -i
+ dh_pycentral -i
+ gzip -9 -c ChangeLog > changelog.gz
+ dh_installchangelogs -i
+ dh_installexamples -i
+ dh_installdocs -i README TODO changelog.gz debian/NEWS.Debian
+ dh_installman -i
+ dh_installemacsen
+ dh_link -i
+ dh_compress -i -X.py -X.ini -X.xml -Xtest
+ dh_fixperms -i
+ dh_installdeb -i
+ dh_gencontrol -i
+ dh_md5sums -i
+ dh_builddeb -i
+
+binary-arch:
+
+binary: binary-indep
+.PHONY: build clean binary binary-indep binary-arch
+
diff --git a/debian/watch b/debian/watch
new file mode 100644
index 0000000..52cc0a2
--- /dev/null
+++ b/debian/watch
@@ -0,0 +1,3 @@
+version=2
+ftp://ftp.logilab.org/pub/pylint/pylint-(.*)\.tar\.gz debian uupdate
+
diff --git a/doc/FAQ.txt b/doc/FAQ.txt
new file mode 100644
index 0000000..9fa496c
--- /dev/null
+++ b/doc/FAQ.txt
@@ -0,0 +1,186 @@
+Frequently Asked Questions
+==========================
+
+
+Question:
+ Is it possible to give file as argument to pylint, instead of module ?
+
+Answer:
+ pylint expects the name of a package or module as argument. As a convenience,
+ you can give to it a file name if it's possible to guess a module name from
+ the file's path, using the python path. Some examples :
+
+ "pylint mymodule.py" should always works since the current working
+ directory is automatically added on top of the python path
+
+ "pylint directory/mymodule.py" will work if "directory" is a python
+ package (i.e. has an __init__.py file) or if "directory" is in the
+ python path.
+
+ "pylint /whatever/directory/mymodule.py" will work if either:
+
+ - "/whatever/directory" is in the python path
+
+ - your cwd is "/whatever/directory"
+
+ - "directory" is a python package and "/whatever" is in the python
+ path
+
+ - "directory" is a python package and your cwd is "/whatever"
+ and so on...
+
+
+
+Question:
+ I'm using psyobj from psyco_ and get a lot of spurious "unused variables
+ messages". Is it normal ?
+
+Answer:
+ Yes. That's actually due to a bug in psyco, making the locals()
+ function for objects inheriting from *psyobj* returning an empty
+ dictionary. For the moment, the only way to fix this is to use the
+ PYLINT_IMPORT environment variable to not use psyco during pylint
+ checking. Sample code ::
+
+ import os
+ try:
+ if os.environ.has_key('PYLINT_IMPORT'):
+ raise ImportError()
+ from psyco.classes import psyobj
+ except ImportError:
+ class psyobj:
+ pass
+
+ NOTICE: this problem should not occurs with pylint >= 0.5 since from
+ this version pylint is not looking anymore for information in living
+ objects (i.e. it doesn't anymore import analysed modules)
+
+
+
+Question:
+ I've a function / method which is a callback where I do not have any
+ control on received argument, and pylint is complaining about unused
+ arguments. What can I do to avoid those warnings ?
+
+Answer:
+ prefix (ui) the callback's name by `cb_`, as in cb_onclick(...). By
+ doing so arguments usage won't be checked. Another solution is to
+ use one of the name defined in the "dummy-variables" configuration
+ variable for unused argument ("_" and "dummy" by default).
+
+
+
+Question:
+ When is pylint considering a class as an interface ?
+
+Answer:
+ A class is considered as an interface if there is a class named
+ "Interface" somewhere in it ancestor's tree.
+
+
+
+Question:
+ When is pylint considering that a class is implementing a given
+ interface ?
+
+Answer:
+ Pylint is using the Zope 2 interfaces conventions, and so is
+ considering that a class is implementing interfaces listed in its
+ __implements__ attribute.
+
+
+
+Question:
+ When is pylint considering a class as an abstract class ?
+
+Answer:
+ A class is considered as an abstract class if at least one of its
+ methods is doing nothing but raising NotImplementedError
+
+
+
+Question:
+ Is there some way to disable some message for a particular module
+ only ?
+
+Answer:
+ Yes, you can disable or enable (globally disabled) message at the
+ module level by adding the corresponding option in a comment at the
+ top of the file: ::
+
+ # pylint: disable-msg=W0401, E0202
+ # pylint: enable-msg=C0302
+
+
+
+Question:
+ I have a mixin class relying on attributes of the mixed class, and I
+ would like to not have the "access to undefined member" message on
+ this class. Is it possible ?
+
+Answer:
+ Yes :o) To do so you have to set the ignore-mixin-members option to
+ "yes" (this is the default value) and to name your mixin class with
+ a name which ends with "mixin" (whatever case)
+
+
+
+Question:
+ Is it possible to locally disable a particular message for a block
+ of code or for a single line of code ?
+
+Answer:
+ Yes, this feature has been added in pylint 0.11. This may be done by
+ adding "#pylint: disable-msg=W0123,E4567" at the desired block level
+ or at the end of the desired line of code
+
+
+
+Question:
+ Where is the persistent data stored to make comparison between
+ two successive runs ?
+
+Answer:
+ Analysis data are stored as pickle file in a directory which is
+ localized using the following rules:
+
+ * value of the PYLINTHOME environment variable if set
+
+ * ".pylint.d" subdirectory of the user's home directory if it is found
+ (not always findable on Windows platforms)
+
+ * ".pylint.d" directory in the current directory
+
+
+
+Question:
+ How can I know the option name (for pylintrc) corresponding to a
+ specific command line option ?
+
+Answer:
+ You can always generate a sample pylintrc file with --generate-rcfile
+ Every option present on the command line before this will be included in
+ the rc file
+
+ For example::
+
+ pylint --disable-msg=W0702,C0103 --class-rgx='[A-Z][a-z]+' --generate-rcfile
+
+
+
+Question:
+ pychecker_ has no problem finding the imports and reporting on problems with
+ them, while pylint seems unable to deal with the same imports. Why ?
+
+Answer:
+ pychecker and pylint use different approaches. pychecker
+ imports the modules and rummages around in the result, hence it sees my
+ mangled sys.path. pylint doesn't import any of the candidate modules and
+ thus doesn't include any of import's side effects (good and bad). It
+ traverses an AST representation of the code.
+
+
+
+
+.. _psyco: http://psyco.sf.net
+.. _pychecker: http://pychecker.sf.net
diff --git a/doc/beginner_pylint_tutorial.txt b/doc/beginner_pylint_tutorial.txt
new file mode 100644
index 0000000..20875d5
--- /dev/null
+++ b/doc/beginner_pylint_tutorial.txt
@@ -0,0 +1,376 @@
+================================================================
+A Beginner's Guide to Code Standards in Python - Pylint Tutorial
+================================================================
+
+:Author: Robert Kirkpatrick
+
+For a detailed description of Pylint, see http://www.logilab.org/project/pylint.
+
+
+Intro
+-----
+
+Beginner to coding standards? Pylint can be your guide to reveal what's really
+going on behind the scenes and help you to become a more aware programmer.
+
+Sharing code is a rewarding endeavor. Putting your code 'out there' can be
+either an act of philanthropy, 'coming of age', or a basic extension of belief
+in open source. Whatever the motivation, your good intentions may not have the
+desired outcome if people find your code hard to use or understand. The Python
+community has formalized some recommended programming styles to help everyone
+write code in a common, agreed-upon style that makes the most sense for shared
+code. This style is captured in PEP-8. Pylint can be a quick and easy way of
+seeing if your code has captured the essence of PEP-8 and is therefore
+'friendly' to other potential users.
+
+Perhaps you're not ready to share your code but you'd like to learn a bit more
+about writing better code and don't know where to start. Pylint can tell you
+where you may have run astray and point you in the direction to figure out what
+you have done and how to do better.
+
+This tutorial is all about approaching coding standards with little or no
+knowledge of in-depth programming or the code standards themselves. It's the
+equivalent of skipping the manual and jumping right in.
+
+My command line prompt for these examples is: ::
+
+ robertk01 Desktop$
+
+Getting Started
+---------------
+
+Running Pylint with no arguments will invoke the help dialogue and give you a
+idea of the arguments available to you. Do that now, i.e.: ::
+
+
+ robertk01 Desktop$ pylint
+ ...
+ a bunch of stuff
+ ...
+
+
+A couple of the options that we'll focus on here are: ::
+
+ Master:
+ --rcfile=<file>
+ Commands:
+ --help-msg=<msg-id>
+ Commands:
+ --help-msg=<msg-id>
+ Message control:
+ --disable-msg=<msg-ids>
+ Reports:
+ --files-output=<y_or_n>
+ --reports=<y_or_n>
+ --include-ids=<y_or_n>
+ --output-format=<format>
+
+Also pay attention to the last bit of help output. This gives you a hint of what
+Pylint is going to 'pick on': ::
+
+ Output:
+ Using the default text output, the message format is :
+ MESSAGE_TYPE: LINE_NUM:[OBJECT:] MESSAGE
+ There are 5 kind of message types :
+ * (C) convention, for programming standard violation
+ * (R) refactor, for bad code smell
+ * (W) warning, for python specific problems
+ * (E) error, for much probably bugs in the code
+ * (F) fatal, if an error occured which prevented pylint from doing
+ further processing.
+
+When Pylint is first run on a fresh piece of code, a common complaint is that it
+is too 'noisy'. The current default configuration is set to enforce all possible
+warnings. We'll use some of the options I noted above to make it suit your
+preferences a bit better (and thus make it 'scream only when needed').
+
+
+Your First Pylint'ing
+---------------------
+
+We'll use a basic python script as fodder for our tutorial. I borrowed
+extensively from the code here: http://www.daniweb.com/code/snippet748.html
+The starting code we will use is called simplecaeser.py and is here in it's
+entirety: ::
+
+ 1 #!/usr/bin/env python
+ 2
+ 3 import string
+ 4
+ 5 shift = 3
+ 6 choice = raw_input("would you like to encode or decode?")
+ 7 word = (raw_input("Please enter text"))
+ 8 letters = string.ascii_letters + string.punctuation + string.digits
+ 9 encoded = ''
+ 10 if choice == "encode":
+ 11 for letter in word:
+ 12 if letter == ' ':
+ 13 encoded = encoded + ' '
+ 14 else:
+ 15 x = letters.index(letter) + shift
+ 16 encoded=encoded + letters[x]
+ 17 if choice == "decode":
+ 18 for letter in word:
+ 19 if letter == ' ':
+ 20 encoded = encoded + ' '
+ 21 else:
+ 22 x = letters.index(letter) - shift
+ 23 encoded = encoded + letters[x]
+ 24
+ 25 print encoded
+
+
+Let's get started.
+
+If we run this: ::
+
+ robertk01 Desktop$ pylint simplecaeser.py
+ No config file found, using default configuration
+ ************* Module simplecaeser
+ C: 1: Missing docstring
+ W: 3: Uses of a deprecated module 'string'
+ C: 5: Invalid name "shift" (should match (([A-Z_][A-Z1-9_]*)|(__.*__))$)
+ C: 6: Invalid name "choice" (should match (([A-Z_][A-Z1-9_]*)|(__.*__))$)
+ C: 7: Invalid name "word" (should match (([A-Z_][A-Z1-9_]*)|(__.*__))$)
+ C: 8: Invalid name "letters" (should match (([A-Z_][A-Z1-9_]*)|(__.*__))$)
+ C: 9: Invalid name "encoded" (should match (([A-Z_][A-Z1-9_]*)|(__.*__))$)
+ C: 16: Operator not preceded by a space
+ encoded=encoded + letters[x]
+ ^
+
+
+ Report
+ ======
+ 19 statements analysed.
+
+ Duplication
+ -----------
+
+ +-------------------------+------+---------+-----------+
+ | |now |previous |difference |
+ +=========================+======+=========+===========+
+ |nb duplicated lines |0 |0 |= |
+ +-------------------------+------+---------+-----------+
+ |percent duplicated lines |0.000 |0.000 |= |
+ +-------------------------+------+---------+-----------+
+
+
+
+ Raw metrics
+ -----------
+
+ +----------+-------+------+---------+-----------+
+ |type |number |% |previous |difference |
+ +==========+=======+======+=========+===========+
+ |code |21 |87.50 |21 |= |
+ +----------+-------+------+---------+-----------+
+ |docstring |0 |0.00 |0 |= |
+ +----------+-------+------+---------+-----------+
+ |comment |1 |4.17 |1 |= |
+ +----------+-------+------+---------+-----------+
+ |empty |2 |8.33 |2 |= |
+ +----------+-------+------+---------+-----------+
+
+
+
+ Statistics by type
+ ------------------
+
+ +---------+-------+-----------+-----------+------------+---------+
+ |type |number |old number |difference |%documented |%badname |
+ +=========+=======+===========+===========+============+=========+
+ |module |1 |1 |= |0.00 |0.00 |
+ +---------+-------+-----------+-----------+------------+---------+
+ |class |0 |0 |= |0.00 |0.00 |
+ +---------+-------+-----------+-----------+------------+---------+
+ |method |0 |0 |= |0.00 |0.00 |
+ +---------+-------+-----------+-----------+------------+---------+
+ |function |0 |0 |= |0.00 |0.00 |
+ +---------+-------+-----------+-----------+------------+---------+
+
+
+
+ Messages by category
+ --------------------
+
+ +-----------+-------+---------+-----------+
+ |type |number |previous |difference |
+ +===========+=======+=========+===========+
+ |convention |7 |7 |= |
+ +-----------+-------+---------+-----------+
+ |refactor |0 |0 |= |
+ +-----------+-------+---------+-----------+
+ |warning |1 |1 |= |
+ +-----------+-------+---------+-----------+
+ |error |0 |0 |= |
+ +-----------+-------+---------+-----------+
+
+
+
+ Messages
+ --------
+
+ +-----------+-----------+
+ |message id |occurences |
+ +===========+===========+
+ |C0103 |5 |
+ +-----------+-----------+
+ |W0402 |1 |
+ +-----------+-----------+
+ |C0322 |1 |
+ +-----------+-----------+
+ |C0111 |1 |
+ +-----------+-----------+
+
+
+
+ Global evaluation
+ -----------------
+ Your code has been rated at 5.79/10
+
+
+Wow. That's a lot of stuff. The first part is the 'messages' section while the
+second part is the 'report' section. There are two points I want to tackle here.
+
+First point is that all the tables of statistics (i.e. the report) are a bit
+overwhelming so I want to silence them. To do that, I will use the "--reports=n" option.
+
+Second, previous experience taught me that the default output for the messages
+needed a bit more info. We can see the first line is: ::
+
+ "C: 1: Missing docstring"
+
+This basically means that line 1 violates a convention 'C'. It's telling me I
+really should have a docstring. I agree, but what if I didn't fully understand
+what rule I violated. Knowing only that I violated a convention isn't much help
+if I'm a newbie. So let's turn on a bit more info by using the option
+"--include-ids=y".
+
+Let's do it again! ::
+
+ robertk01 Desktop$ pylint --reports=n --include-ids=y simplecaeser.py
+ No config file found, using default configuration
+ ************* Module simplecaeser
+ C0111: 1: Missing docstring
+ W0402: 3: Uses of a deprecated module 'string'
+ C0103: 5: Invalid name "shift" (should match (([A-Z_][A-Z1-9_]*)|(__.*__))$)
+ C0103: 6: Invalid name "choice" (should match (([A-Z_][A-Z1-9_]*)|(__.*__))$)
+ C0103: 7: Invalid name "word" (should match (([A-Z_][A-Z1-9_]*)|(__.*__))$)
+ C0103: 8: Invalid name "letters" (should match (([A-Z_][A-Z1-9_]*)|(__.*__))$)
+ C0103: 9: Invalid name "encoded" (should match (([A-Z_][A-Z1-9_]*)|(__.*__))$)
+ C0322: 16: Operator not preceded by a space
+ encoded=encoded + letters[x]
+
+Oooh. I like that better. Now I know that I violated the convention number
+C0111 and now I can read up a bit more about that. Let's go back to the
+command line and try this: ::
+
+ robertk01 Desktop$ pylint --help-msg=C0111
+ No config file found, using default configuration
+ :C0111: *Missing docstring*
+ Used when a module, function, class or method has no docstring. Some special
+ methods like __init__ doesn't necessary require a docstring. This message
+ belongs to the basic checker.
+
+Yeah, ok. That one was a bit of a no-brainer but I have run into error messages
+that left me with no clue about what went wrong, simply because I was unfamiliar
+with the underlying mechanism of code theory. One error that puzzled my newbie
+mind was: ::
+
+ :R0902: *Too many instance attributes (%s/%s)*
+
+I get it now thanks to Pylint pointing it out to me. If you don't get that one,
+pour a fresh cup of coffee and look into it - let your programmer mind grow!
+
+
+The Next Step
+-------------
+
+Now that we got some configuration stuff out of the way, let's see what we can
+do with the remaining warnings.
+
+If we add a docstring to describe what the code is meant to do that will help.
+I'm also going to be a bit cowboy and ignore the W0402 message because I like to
+take risks in life. A deprecation warning means that future versions of Python
+may not support that code so my code may break in the future. There are 5 C0103
+messages that we will get to later. Lastly, I violated the convention of using
+spaces around an operator such as "=" so I'll fix that too. To sum up, I'll add
+a docstring to line 2, put spaces around the = sign on line 16 and use the
+"--disable-msg=W0402" to ignore the deprecation warning.
+
+Here's the updated code: ::
+
+ 1 #!/usr/bin/env python
+ 2 """This script prompts a user to enter a messsage to encode or decode
+ 3 using a classic Caeser shift substitution (3 letter shift)"""
+ 4
+ 5 import string
+ 6
+ 7 shift = 3
+ 8 choice = raw_input("would you like to encode or decode?")
+ 9 word = (raw_input("Please enter text"))
+ 10 letters = string.ascii_letters + string.punctuation + string.digits
+ 11 encoded = ''
+ 12 if choice == "encode":
+ 13 for letter in word:
+ 14 if letter == ' ':
+ 15 encoded = encoded + ' '
+ 16 else:
+ 17 x = letters.index(letter) + shift
+ 18 encoded = encoded + letters[x]
+ 19 if choice == "decode":
+ 20 for letter in word:
+ 21 if letter == ' ':
+ 22 encoded = encoded + ' '
+ 23 else:
+ 24 x = letters.index(letter) - shift
+ 25 encoded = encoded + letters[x]
+ 26
+ 27 print encoded
+
+And here's what happens when we run it with our --disable-msg=W0402 option: ::
+
+ robertk01 Desktop$ pylint --reports=n --include-ids=y --disable-msg=W0402 simplecaeser.py
+ No config file found, using default configuration
+ ************* Module simplecaeser
+ C0103: 7: Invalid name "shift" (should match (([A-Z_][A-Z1-9_]*)|(__.*__))$)
+ C0103: 8: Invalid name "choice" (should match (([A-Z_][A-Z1-9_]*)|(__.*__))$)
+ C0103: 9: Invalid name "word" (should match (([A-Z_][A-Z1-9_]*)|(__.*__))$)
+ C0103: 10: Invalid name "letters" (should match (([A-Z_][A-Z1-9_]*)|(__.*__))$)
+ C0103: 11: Invalid name "encoded" (should match (([A-Z_][A-Z1-9_]*)|(__.*__))$)
+
+Nice! We're down to just the C0103 messages.
+
+There are fairly well defined conventions around naming things like instance
+variables, functions, classes, etc. The conventions focus on the use of
+UPPERCASE and lowercase as well as the characters that separate multiple words
+in the name. This lends itself well to checking via a regular expression, thus
+the "should match (([A-Z\_][A-Z1-9\_]*)|(__.*__))$".
+
+In this case Pylint is telling me that those variables appear to be constants
+and should be all UPPERCASE. This rule is in fact a naming convention that is
+specific to the folks at Logilab who created Pylint. That is the way they have
+chosen to name those variables. You too can create your own in-house naming
+conventions but for the purpose of this tutorial, we want to stick to the PEP-8
+standard. In this case, the variables I declared should follow the convention
+of all lowercase. The appropriate rule would be something like:
+"should match [a-z\_][a-z0-9\_]{2,30}$". Notice the lowercase letters in the
+regular expression (a-z versus A-Z).
+
+If we run that rule using a --const-rgx='[a-z\_][a-z0-9\_]{2,30}$' option, it
+will now be quite quiet: ::
+
+ robertk01 Desktop$ pylint --reports=n --include-ids=y --disable-msg=W0402 --const-rgx='[a-z_][a-z0-9_]{2,30}$' simplecaeser.py
+ No config file found, using default configuration
+
+Regular expressions can be quite a beast so take my word on this particular
+example but go ahead and read up on them if you want.
+
+It would really be a pain in the butt to have to use all these options on the
+command line all the time. That's what the rc file is for. We can configure
+our Pylint to store our options for us so we don't have to declare them on the
+command line. Using the rc file is a nice way of formalizing your rules and
+quickly sharing them with others and/or forcing your style on them. Go ahead,
+conquer the standards world by spreading your rc file...I dare you.
+
+That's it for the basic intro. More tutorials will follow.
diff --git a/doc/features.txt b/doc/features.txt
new file mode 100644
index 0000000..3ef5136
--- /dev/null
+++ b/doc/features.txt
@@ -0,0 +1,735 @@
+PyLint features
+===============
+
+.. contents::
+
+General options
+~~~~~~~~~~~~~~~
+:rcfile:
+ Specify a configuration file.
+:init-hook:
+ Python code to execute, usually for sys.path manipulation such as
+ pygtk.require().
+:rpython-mode:
+ enable the rpython checker which is disabled by default
+:errors-only:
+ In debug mode, checkers without error messages are disabled and for others,
+ only the ERROR messages are displayed, and no reports are done by default
+:profile:
+ Profiled execution.
+:ignore:
+ Add <file or directory> to the black list. It should be a base name, not a
+ path. You may set this option multiple times.
+ Default: CVS
+:persistent:
+ Pickle collected data for later comparisons.
+ Default: yes
+:cache-size:
+ Set the cache size for astng objects.
+ Default: 500
+:load-plugins:
+ List of plugins (as comma separated values of python modules names) to load,
+ usually to register additional checkers.
+
+Commands options
+~~~~~~~~~~~~~~~~
+:help-msg:
+ Display a help message for the given message id and exit. The value may be a
+ comma separated list of message ids.
+:list-msgs:
+ Generate pylint's full documentation.
+:generate-rcfile:
+ Generate a sample configuration file according to the current configuration.
+ You can put other options before this one to get them in the generated
+ configuration.
+:generate-man:
+ Generate pylint's man page.
+
+Messages control options
+~~~~~~~~~~~~~~~~~~~~~~~~
+:enable-checker:
+ Enable only checker(s) with the given id(s). This option conflicts with the
+ disable-checker option
+:disable-checker:
+ Enable all checker(s) except those with the given id(s). This option
+ conflicts with the enable-checker option
+:enable-msg-cat:
+ Enable all messages in the listed categories.
+:disable-msg-cat:
+ Disable all messages in the listed categories.
+:enable-msg:
+ Enable the message(s) with the given id(s).
+:disable-msg:
+ Disable the message(s) with the given id(s).
+
+Reports options
+~~~~~~~~~~~~~~~
+:output-format:
+ Set the output format. Available formats are text, parseable, colorized, msvs
+ (visual studio) and html
+ Default: text
+:include-ids:
+ Include message's id in output
+:files-output:
+ 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:
+ Tells wether to display a full report or only the messages
+ Default: yes
+:evaluation:
+ 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
+ respectivly contain the number of errors / warnings messages and the total
+ number of statements analyzed. This is used by the global evaluation report
+ (R0004).
+ Default: 10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)
+:comment:
+ Add a comment according to your evaluation note. This is used by the global
+ evaluation report (R0004).
+:enable-report:
+ Enable the report(s) with the given id(s).
+:disable-report:
+ Disable the report(s) with the given id(s).
+
+Main messages
+~~~~~~~~~~~~~
+:E0001:
+ Used when a syntax error is raised for a module.
+:E0011: *Unrecognized file option %r*
+ Used when an unknown inline option is encountered.
+:E0012: *Bad option value %r*
+ Used when a bad value for an inline option is encountered.
+:I0001: *Unable to run raw checkers on built-in module %s*
+ Used to inform that a built-in module has not been checked using the raw
+ checkers.
+:I0010: *Unable to consider inline option %r*
+ Used when an inline option is either badly formatted or can't be used inside
+ modules.
+:I0011: *Locally disabling %s*
+ Used when an inline option disables a message or a messages category.
+:I0012: *Locally enabling %s*
+ Used when an inline option enables a message or a messages category.
+:I0013: *Ignoring entire file*
+ Used to inform that the file will not be checked
+:F0001:
+ Used when an error occured preventing the analysis of a module (unable to find
+ it for instance).
+:F0002: *%s: %s*
+ Used when an unexpected error occured while building the ASTNG representation.
+ This is usually accompanied by a traceback. Please report such errors !
+:F0003: *ignored builtin module %s*
+ Used to indicate that the user asked to analyze a builtin module which has
+ been skipped.
+:F0004: *unexpected infered value %s*
+ Used to indicate that some value of an unexpected type has been infered.
+
+Main reports
+~~~~~~~~~~~~
+:R0001: Messages by category
+:R0002: % errors / warnings by module
+:R0003: Messages
+:R0004: Global evaluation
+
+
+Basic checker
+-------------
+checks for :
+* doc strings
+* modules / classes / functions / methods / arguments / variables name
+* number of arguments, local variables, branchs, returns and statements in
+functions, methods
+* required module attributes
+* dangerous default values as arguments
+* redefinition of function / method / class
+* uses of the global statement
+
+Options
+~~~~~~~
+:required-attributes:
+ Required attributes for module, separated by a comma
+:no-docstring-rgx:
+ Regular expression which should only match functions or classes name which do
+ not require a docstring
+ Default: __.*__
+:module-rgx:
+ Regular expression which should only match correct module names
+ Default: `(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$`
+:const-rgx:
+ Regular expression which should only match correct module level names
+ Default: `(([A-Z_][A-Z1-9_]*)|(__.*__))$`
+:class-rgx:
+ Regular expression which should only match correct class names
+ Default: `[A-Z_][a-zA-Z0-9]+$`
+:function-rgx:
+ Regular expression which should only match correct function names
+ Default: `[a-z_][a-z0-9_]{2,30}$`
+:method-rgx:
+ Regular expression which should only match correct method names
+ Default: `[a-z_][a-z0-9_]{2,30}$`
+:attr-rgx:
+ Regular expression which should only match correct instance attribute names
+ Default: `[a-z_][a-z0-9_]{2,30}$`
+:argument-rgx:
+ Regular expression which should only match correct argument names
+ Default: `[a-z_][a-z0-9_]{2,30}$`
+:variable-rgx:
+ Regular expression which should only match correct variable names
+ Default: `[a-z_][a-z0-9_]{2,30}$`
+:inlinevar-rgx:
+ Regular expression which should only match correct list comprehension /
+ generator expression variable names
+ Default: `[A-Za-z_][A-Za-z0-9_]*$`
+:good-names:
+ Good variable names which should always be accepted, separated by a comma
+ Default: i,j,k,ex,Run,_
+:bad-names:
+ Bad variable names which should always be refused, separated by a comma
+ Default: foo,bar,baz,toto,tutu,tata
+:bad-functions:
+ List of builtins function names that should not be used, separated by a comma
+ Default: map,filter,apply,input
+
+Messages
+~~~~~~~~
+:E0100: *__init__ method is a generator*
+ Used when the special class method __init__ is turned into a generator by a
+ yield in its body.
+:E0101: *Explicit return in __init__*
+ Used when the special class method __init__ has an explicit return value.
+:E0102: *%s already defined line %s*
+ Used when a function / class / method is redefined.
+:E0103: *%r not properly in loop*
+ Used when break or continue keywords are used outside a loop.
+:E0104: *return outside function*
+ Used when a "return" statement is found outside a function or method.
+:E0105: *yield outside function*
+ Used when a "yield" statement is found outside a function or method.
+:E0106: *return with argument inside generator*
+ Used when a "return" statement with an argument is found outside in a
+ generator function or method (e.g. with some "yield" statements).
+:W0101: *Unreachable code*
+ Used when there is some code behind a "return" or "raise" statement, which
+ will never be accessed.
+:W0102: *Dangerous default value %s as argument*
+ 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*
+ Used when a statement doesn't have (or at least seems to) any effect.
+:W0105: *String statement has no effect*
+ 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: *Unnecessary semicolon*
+ Used when a statement is endend by a semi-colon (";"), which isn't necessary
+ (that's python, not C ;).
+:W0107: *Unnecessary pass statement*
+ Used when a "pass" statement that can be avoided is encountered.)
+:W0122: *Use of the exec statement*
+ Used when you use the "exec" statement, to discourage its usage. That doesn't
+ mean you can not use it !
+:W0141: *Used builtin function %r*
+ 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*
+ 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.
+:C0102: *Black listed name "%s"*
+ Used when the name is listed in the black list (unauthorized names).
+:C0103: *Invalid name "%s" (should match %s)*
+ Used when the name doesn't match the regular expression associated to its type
+ (constant, variable, class...).
+:C0111: *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 docstring*
+ Used when a module, function, class or method has an empty docstring (it would
+ be too easy ;).
+:C0121: *Missing required attribute "%s"*
+ Used when an attribute required for modules is missing.
+
+Reports
+~~~~~~~
+:R0101: Statistics by type
+
+
+Typecheck checker
+-----------------
+try to find bugs in the code using type inference
+
+Options
+~~~~~~~
+:ignore-mixin-members:
+ Tells wether missing members accessed in mixin class should be ignored. A
+ mixin class is detected if its name ends with "mixin" (case insensitive).
+ Default: yes
+:ignored-classes:
+ List of classes names for which member attributes should not be checked
+ (useful for classes with attributes dynamicaly set).
+ Default: SQLObject
+:zope:
+ When zope mode is activated, consider the acquired-members option to ignore
+ access to some undefined attributes.
+:acquired-members:
+ List of members which are usually get through zope's acquisition mecanism and
+ so shouldn't trigger E0201 when accessed (need zope=yes to be considered).
+ Default: REQUEST,acl_users,aq_parent
+
+Messages
+~~~~~~~~
+:E1101: *%s %r has no %r member*
+ Used when a variable is accessed for an unexistant member.
+:E1102: *%s is not callable*
+ Used when an object being called has been infered to a non callable object
+:E1103: *%s %r has no %r member (but some types could not be inferred)*
+ Used when a variable is accessed for an unexistant member, but astng was not
+ able to interpret all possible types of this variable.
+:E1111: *Assigning to function call which doesn't return*
+ Used when an assigment is done on a function call but the infered function
+ doesn't return anything.
+:W1111: *Assigning to function call which only returns None*
+ Used when an assigment is done on a function call but the infered function
+ returns nothing but None.
+
+
+Variables checker
+-----------------
+checks for
+* unused variables / imports
+* undefined variables
+* redefinition of variable from builtins or from an outer scope
+* use of variable before assigment
+
+Options
+~~~~~~~
+:init-import:
+ Tells wether we should check for unused import in __init__ files.
+:dummy-variables-rgx:
+ A regular expression matching names used for dummy variables (i.e. not used).
+ Default: _|dummy
+:additional-builtins:
+ List of additional names supposed to be defined in builtins. Remember that
+ you should avoid to define new builtins when possible.
+
+Messages
+~~~~~~~~
+:E0601: *Using variable %r before assignment*
+ Used when a local variable is accessed before it's assignment.
+:E0602: *Undefined variable %r*
+ Used when an undefined variable is accessed.
+:E0611: *No name %r in module %r*
+ Used when a name cannot be found in a module.
+:W0601: *Global variable %r undefined at the module level*
+ 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 assigment is done*
+ Used when a variable is defined through the "global" statement but no
+ assigment to this variable is done.
+:W0603: *Using the 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*
+ Used when you use the "global" statement at the module level since it has no
+ effect
+:W0611: *Unused import %s*
+ Used when an imported module or variable is not used.
+:W0612: *Unused variable %r*
+ Used when a variable is defined but not used.
+:W0613: *Unused argument %r*
+ Used when a function or method argument is not used.
+:W0614: *Unused import %s from 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)*
+ Used when a variable's name hide a name defined in the outer scope.
+:W0622: *Redefining built-in %r*
+ Used when a variable or function override a built-in.
+:W0631: *Using possibly undefined loop variable %r*
+ 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.
+
+
+Classes checker
+---------------
+checks for :
+* methods without self as first argument
+* overridden methods signature
+* access only to existant members via self
+* attributes not defined in the __init__ method
+* supported interfaces implementation
+* unreachable code
+
+Options
+~~~~~~~
+:ignore-iface-methods:
+ 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.
+ Default: isImplementedBy,deferred,extends,names,namesAndDescriptions,queryDescriptionFor,getBases,getDescriptionFor,getDoc,getName,getTaggedValue,getTaggedValueTags,isEqualOrExtendedBy,setTaggedValue,isImplementedByInstancesOf,adaptWith,is_implemented_by
+:defining-attr-methods:
+ List of method names used to declare (i.e. assign) instance attributes.
+ Default: __init__,__new__,setUp
+
+Messages
+~~~~~~~~
+:E0202: *An attribute inherited from %s hide this method*
+ Used when a class defines a method which is hiden by an instance attribute
+ from an ancestor class.
+:E0203: *Access to member %r before its definition line %s*
+ Used when an instance member is accessed before it's actually assigned.
+:E0211: *Method has no 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*
+ Used when a method has an attribute different the "self" as first argument.
+ This is considered as an error since this is a soooo common convention that
+ you should'nt break it!
+:E0221: *Interface resolved to %s is not a class*
+ Used when a class claims to implement an interface which is not a class.
+:E0222: *Missing method %r from %s interface*
+ Used when a method declared in an interface is missing from a class
+ implementing this interface
+:W0201: *Attribute %r defined outside __init__*
+ Used when an instance attribute is defined outside the __init__ method.
+:W0211: *Static method with %r as first argument*
+ Used when a static method has "self" or "cls" as first argument.
+:W0212: *Access to a protected member %s of a client class*
+ 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.
+:W0221: *Arguments number differs from %s method*
+ 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 method*
+ 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*
+ Used when an abstract method (ie raise NotImplementedError) is not overridden
+ in concrete class.
+:W0231: *__init__ method from base class %r is 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*
+ Used when a class has no __init__ method, neither its parent classes.
+:W0233: *__init__ method from a non direct base class %r is called*
+ Used when an __init__ method is called on a class which is not in the direct
+ ancestors for the analysed class.
+:R0201: *Method could be a function*
+ Used when a method doesn't use its bound instance, and so could be written as
+ a function.
+:C0202: *Class method should have "cls" as first argument*
+ Used when a class method has an attribute different than "cls" as first
+ argument, to easily differentiate them from regular instance methods.
+:C0203: *Metaclass method should have "mcs" as first argument*
+ Used when a metaclass method has an attribute different the "mcs" as first
+ argument.
+:F0202: *Unable to check methods signature (%s / %s)*
+ Used when PyLint has been unable to check methods signature compatibility for
+ an unexpected raison. Please report this kind if you don't make sense of it.
+:F0220: *failed to resolve interfaces implemented by %s (%s)*
+ Used when a PyLint as failed to find interfaces implemented by a class
+
+
+Design checker
+--------------
+checks for sign of poor/misdesign:
+* number of methods, attributes, local variables...
+* size, complexity of functions, methods
+
+Options
+~~~~~~~
+:max-args:
+ Maximum number of arguments for function / method
+ Default: 5
+:max-locals:
+ Maximum number of locals for function / method body
+ Default: 15
+:max-returns:
+ Maximum number of return / yield for function / method body
+ Default: 6
+:max-branchs:
+ Maximum number of branch for function / method body
+ Default: 12
+:max-statements:
+ Maximum number of statements in function / method body
+ Default: 50
+:max-parents:
+ Maximum number of parents for a class (see R0901).
+ Default: 7
+:max-attributes:
+ Maximum number of attributes for a class (see R0902).
+ Default: 7
+:min-public-methods:
+ Minimum number of public methods for a class (see R0903).
+ Default: 2
+:max-public-methods:
+ Maximum number of public methods for a class (see R0904).
+ Default: 20
+
+Messages
+~~~~~~~~
+:R0901: *Too many ancestors (%s/%s)*
+ Used when class has too many parent classes, try to reduce this to get a more
+ simple (and so easier to use) class.
+:R0902: *Too many instance attributes (%s/%s)*
+ Used when class has too many instance attributes, try to reduce this to get a
+ more simple (and so easier to use) class.
+:R0903: *Too few public methods (%s/%s)*
+ Used when class has too few public methods, so be sure it's really worth it.
+:R0904: *Too many public methods (%s/%s)*
+ Used when class has too many public methods, try to reduce this to get a more
+ simple (and so easier to use) class.
+:R0911: *Too many return statements (%s/%s)*
+ Used when a function or method has too many return statement, making it hard
+ to follow.
+:R0912: *Too many branches (%s/%s)*
+ Used when a function or method has too many branches, making it hard to
+ follow.
+:R0913: *Too many arguments (%s/%s)*
+ Used when a function or method takes too many arguments.
+:R0914: *Too many local variables (%s/%s)*
+ Used when a function or method has too many local variables.
+:R0915: *Too many statements (%s/%s)*
+ Used when a function or method has too many statements. You should then split
+ it in smaller functions / methods.
+:R0921: *Abstract class not referenced*
+ Used when an abstract class is not used as ancestor anywhere.
+:R0922: *Abstract class is only referenced %s times*
+ Used when an abstract class is used less than X times as ancestor.
+:R0923: *Interface not implemented*
+ Used when an interface class is not implemented anywhere.
+
+
+Imports checker
+---------------
+checks for
+* external modules dependencies
+* relative / wildcard imports
+* cyclic imports
+* uses of deprecated modules
+
+Options
+~~~~~~~
+:deprecated-modules:
+ Deprecated modules which should not be used, separated by a comma
+ Default: regsub,string,TERMIOS,Bastion,rexec
+:import-graph:
+ Create a graph of every (i.e. internal and external) dependencies in the
+ given file (report R0402 must not be disabled)
+:ext-import-graph:
+ Create a graph of external dependencies in the given file (report R0402 must
+ not be disabled)
+:int-import-graph:
+ Create a graph of internal dependencies in the given file (report R0402 must
+ not be disabled)
+
+Messages
+~~~~~~~~
+:W0401: *Wildcard import %s*
+ Used when `from module import *` is detected.
+:W0402: *Uses of a deprecated module %r*
+ Used a module marked as deprecated is imported.
+:W0403: *Relative import %r*
+ Used when an import relative to the package directory is detected.
+:W0404: *Reimport %r (imported line %s)*
+ Used when a module is reimported multiple times.
+:W0406: *Module import itself*
+ Used when a module is importing itself.
+:W0410: *__future__ import is not the first non docstring statement*
+ Python 2.5 and greater require __future__ import to be the first non docstring
+ statement in the module.
+:R0401: *Cyclic import (%s)*
+ Used when a cyclic import between two or more modules is detected.
+:F0401: *Unable to import %r (%s)*
+ Used when pylint has been unable to import a module.
+
+Reports
+~~~~~~~
+:R0401: External dependencies
+:R0402: Modules dependencies graph
+
+
+Newstyle checker
+----------------
+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
+* raising a new style class as exception
+
+Messages
+~~~~~~~~
+:E1001: *Use __slots__ on an old style class*
+ Used when an old style class use the __slots__ attribute.
+:E1002: *Use super on an old style class*
+ Used when an old style class use the super builtin.
+:E1003: *Bad first argument %r given to super class*
+ Used when another argument than the current class is given as first argument
+ of the super builtin.
+:E1010: *Raising a new style class*
+ Used when a new style class is raised since it's not possible with python <
+ 2.5.
+:W1001: *Use of "property" on an old style 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
+:W1010: *Exception doesn't inherit from standard "Exception" class*
+ Used when a custom exception class is raised but doesn't inherit from the
+ builtin "Exception" class.
+
+
+Exceptions checker
+------------------
+checks for
+* excepts without exception filter
+* string exceptions
+
+Messages
+~~~~~~~~
+:E0701: *Bad except clauses order (%s)*
+ 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, instances or string are allowed*
+ Used when something which is neither a class, an instance or a string is
+ raised (i.e. a `TypeError` will be raised).
+:W0701: *Raising a string exception*
+ Used when a string exception is raised.
+:W0702: *No exception type(s) specified*
+ Used when an except clause doesn't specify exceptions type to catch.
+:W0703: *Catch "Exception"*
+ Used when an except catches Exception instances.
+:W0704: *Except doesn't do anything*
+ Used when an except clause does nothing but "pass" and there is no "else"
+ clause.
+:W0706: *Identifier %s used to raise an exception is assigned to %s*
+ Used when a variable used to raise an exception is initially assigned to a
+ value which can't be used as an exception.
+
+
+Format checker
+--------------
+checks for :
+* unauthorized constructions
+* strict indentation
+* line length
+* use of <> instead of !=
+
+Options
+~~~~~~~
+:max-line-length:
+ Maximum number of characters on a single line.
+ Default: 80
+:max-module-lines:
+ Maximum number of lines in a module
+ Default: 1000
+:indent-string:
+ String used as indentation unit. This is usually " " (4 spaces) or "\t" (1
+ tab).
+ Default: ' '
+
+Messages
+~~~~~~~~
+:W0311: *Bad indentation. Found %s %s, expected %s*
+ Used when an unexpected number of indentation's tabulations or spaces has been
+ found.
+:W0312: *Found indentation with %ss instead of %ss*
+ Used when there are some mixed tabs and spaces in a module.
+:W0331: *Use of the <> operator*
+ Used when the deprecated "<>" operator is used instead of "!=".
+:W0332: *Use l as long integer identifier*
+ 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"
+:W0333: *Use of the `` operator*
+ Used when the deprecated "``" (backtick) operator is used instead of the str()
+ function.
+:C0301: *Line too long (%s/%s)*
+ Used when a line is longer than a given number of characters.
+:C0302: *Too many lines in module (%s)*
+ Used when a module has too much lines, reducing its readibility.
+:C0321: *More than one statement on a single line*
+ Used when more than on statement are found on the same line.
+:C0322: *Operator not preceded by a space*
+ Used when one of the following operator (!= | <= | == | >= | < | > | = | \+= |
+ -= | \*= | /= | %) is not preceded by a space.
+:C0323: *Operator not followed by a space*
+ Used when one of the following operator (!= | <= | == | >= | < | > | = | \+= |
+ -= | \*= | /= | %) is not followed by a space.
+:C0324: *Comma not followed by a space*
+ Used when a comma (",") is not followed by a space.
+:F0321: *Format detection error in %r*
+ Used when an unexpected error occured in bad format detection.Please report
+ the error if it occurs.
+
+
+Miscellaneous checker
+---------------------
+checks for:
+* warning notes in the code like FIXME, XXX
+* PEP 263: source code with non ascii character but no encoding declaration
+
+Options
+~~~~~~~
+:notes:
+ List of note tags to take in consideration, separated by a comma.
+ Default: FIXME,XXX,TODO
+
+Messages
+~~~~~~~~
+:E0501: *Non ascii characters found but no encoding specified (PEP 263)*
+ Used when some non ascii characters are detected but now encoding is
+ specified, as explicited in the PEP 263.
+:E0502: *Wrong encoding specified (%s)*
+ Used when a known encoding is specified but the file doesn't seem to be
+ actually in this encoding.
+:E0503: *Unknown encoding specified (%s)*
+ Used when an encoding is specified, but it's unknown to Python.
+:W0511:
+ Used when a warning note as FIXME or XXX is detected.
+
+
+Metrics checker
+---------------
+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
+
+Reports
+~~~~~~~
+:R0701: Raw metrics
+
+
+Similarities checker
+--------------------
+checks for similarities and duplicated code. This computation may be
+memory / CPU intensive, so you should disable it if you experiments some
+problems.
+
+Options
+~~~~~~~
+:min-similarity-lines:
+ Minimum lines number of a similarity.
+ Default: 4
+:ignore-comments:
+ Ignore comments when computing similarities.
+ Default: yes
+:ignore-docstrings:
+ Ignore docstrings when computing similarities.
+ Default: yes
+
+Messages
+~~~~~~~~
+:R0801: *Similar lines in %s files*
+ 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.
+
+Reports
+~~~~~~~
+:R0801: Duplication
+
+
diff --git a/doc/makefile b/doc/makefile
new file mode 100644
index 0000000..4e0481b
--- /dev/null
+++ b/doc/makefile
@@ -0,0 +1,44 @@
+MKHTML=mkdoc
+MKHTML_OPT=--doctype article --param toc.section.depth=1 --target html --stylesheet single-file
+
+SRC=.
+
+
+all: manual.html quickstart.html features.html FAQ.html beginner_pylint_tutorial.html examples man
+
+FAQ.html: ${SRC}/FAQ.txt
+ ${MKHTML} ${MKHTML_OPT} ${SRC}/FAQ.txt
+
+quickstart.html: ${SRC}/quickstart.txt
+ ${MKHTML} ${MKHTML_OPT} ${SRC}/quickstart.txt
+
+manual.html: ${SRC}/manual.txt ${SRC}/FAQ.txt
+ ${MKHTML} ${MKHTML_OPT} ${SRC}/manual.txt
+
+beginner_pylint_tutorial.html: ${SRC}/beginner_pylint_tutorial.txt
+ ${MKHTML} ${MKHTML_OPT} ${SRC}/beginner_pylint_tutorial.txt
+
+features.html:
+ chmod u+w ${SRC}/features.txt
+ echo "PyLint features" > ${SRC}/features.txt
+ echo "===============" >> ${SRC}/features.txt
+ echo "" >> ${SRC}/features.txt
+ echo ".. generated by pylint --list-msgs" >> ${SRC}/features.txt
+ echo "" >> ${SRC}/features.txt
+ echo ".. contents::" >> ${SRC}/features.txt
+ echo "" >> ${SRC}/features.txt
+ pylint --list-msgs >> ${SRC}/features.txt
+ ${MKHTML} ${MKHTML_OPT} ${SRC}/features.txt
+
+examples:
+ chmod u+w ../examples/pylintrc
+ pylint --generate-rcfile > ../examples/pylintrc
+
+man:
+ chmod u+w ../man/pylint.1
+ pylint --generate-man > ../man/pylint.1
+
+clean:
+ rm -f *.html
+
+.PHONY: features.html
diff --git a/doc/manual.txt b/doc/manual.txt
new file mode 100644
index 0000000..1b3c1eb
--- /dev/null
+++ b/doc/manual.txt
@@ -0,0 +1,637 @@
+==================
+Pylint User Manual
+==================
+
+:Author: Sylvain Thénault
+:Author: Alexandre Fayolle
+:Organization: Logilab
+
+.. contents::
+
+
+This document is meant to be the reference user manual for Pylint_. This is a
+work in progress so some sections or parts may be missing (sometimes marked by a
+XXX). If you think it's lacking some important information, please talk about
+it on the python-projects mailing list (see the `Mailing lists`_ section for
+more information about the list).
+
+.. _Pylint: http://www.logilab.org/project/name/pylint
+
+
+Introduction
+============
+
+What is pylint?
+---------------
+
+Pylint is a tool that checks for errors in python code, tries to enforce a
+coding standard and looks for smelling code. This is similar but nevertheless
+different from what pychecker_ provides, especially since pychecker explicitely
+does not bother with coding style. The default coding style used by pylint is
+close to `PEP 008`_ (aka `Guido's style guide`_). For more information about
+code smells, refer to Martin Fowler's `refactoring book`_
+
+One important thing to note is that Pylint isn't smarter than you are: it may
+warn you about things that you have conscientiously done. That's for example
+because it tries to detect things that may be dangerous in a context, but maybe
+not in others, or because it checks for some things that you don't care
+about. Generally, you shouldn't expect pylint to be totally quiet about your
+code, so don't necessarily be alarmed if it gives you a hell lot of messages for
+your proudly(XXX) project ;)
+
+Pylint will display a number of messages as it analyzes the code, as well as
+some statistics about the number of warnings and errors found in different
+files. The messages are classified under various categories such as errors and
+warnings (more below). If you run pylint twice, it will display the statistics
+from the previous run together with the ones from the current run, so that you
+can see if the code has improved or not.
+
+Last but not least, the code is given an overall mark, based on the number an
+severity of the warnings and errors. This has proven to be very motivating for
+programmers.
+
+.. _pychecker: http://pychecker.sf.net
+.. _`PEP 008`: http://www.python.org/dev/peps/pep-0008/
+.. _`Guido's style guide`: http://www.python.org/doc/essays/styleguide.html
+.. _`refactoring book`: http://www.refactoring.com/
+
+Installation
+------------
+
+Dependancies
+''''''''''''
+Pylint requires the latest `logilab-astng`_ and `logilab-common`_
+packages. It should be compatible with any python version greater than
+2.2.0 (python 2.2 users will have to install the optik_ package).
+
+.. _`logilab-astng`: http://www.logilab.org/project/name/astng
+.. _`logilab-common`: http://www.logilab.org/project/name/common
+.. _optik: http://optik.sourceforge.net/
+
+
+Distributions
+'''''''''''''
+The source tarball is available at ftp://ftp.logilab.fr/pub/pylint.
+
+You may apt-get a debian package by adding ::
+
+ deb ftp://ftp.logilab.org/pub/debian unstable/
+
+to your */etc/apt/sources.list* file. Pylint is also available in the
+standard Debian distribution (but add our public debian repository
+anyway if you want to get the latest releases and upgrades earlier)
+
+Contributed RPM packages for pylint and logilab-common are available at
+ftp://ftp.nest.pld-linux.org/test.
+
+Pylint is also available in Gentoo, Fedora 4, Ubuntu, FreeBSD, Darwin
+(and maybe other, if si drop us a note please!).
+
+
+Source distribution installation
+''''''''''''''''''''''''''''''''
+From the source distribution, extract the tarball, go to the extracted
+directory and simply run ::
+
+ python setup.py install
+
+You'll have to install dependancies in a similar way.
+
+Windows users may get valuable information about pylint installation on
+`this page`_.
+
+.. _`this page`: http://thinkhole.org/wp/2006/01/16/installing-pylint-on-windows/
+
+
+Note for Windows users
+''''''''''''''''''''''
+
+On Windows, once you have installed pylint, the command line usage is ::
+
+ pylint.bat [options] module_or_package
+
+But this will only work if *pylint.bat* is either in the current
+directory, or on your system path. (*setup.py* install install *python.bat*
+to the *Scripts* subdirectory of your Python installation -- e.g.
+C:\Python24\Scripts.) You can do any of the following to solve this:
+
+1. change to the appropriate directory before running pylint.bat
+
+2. add the Scripts directory to your path statement in your autoexec.bat
+ file (this file is found in the root directory of your boot-drive)
+
+3. create a 'redirect' batch file in a directory actually on your
+ systems path
+
+To effect (2), simply append the appropriate directory name to the PATH=
+statement in autoexec.bat. Be sure to use the Windows directory
+separator of ';' between entries. Then, once you have rebooted (this is
+necessary so that the new path statement will take effect when
+autoexec.bat is run), you will be able to invoke PyLint with
+pylint.bat on the command line.
+
+(3) is the best solution. Once done, you can call pylint at the command
+line without the .bat, just as do non-Windows users by typing: ::
+
+ pylint [options] module_or_package
+
+To effect option (3), simply create a plain text file pylint.bat with
+the single line: ::
+
+ C:\PythonDirectory\Scripts\pylint.bat
+
+(where PythonDirectory is replaced by the actual Python installation
+directory on your system -- e.g. C:\Python24\Scripts\pylint.bat).
+
+
+Invoking pylint
+---------------
+
+Pylint is meant to be called from the command line. The usage is ::
+
+ pylint [options] module_or_package
+
+You should give pylint the name of a Python package or module. Pylint
+will ``import`` this package or module, so you should pay attention to
+your ``PYTHONPATH``, since it is a common error to analyze an
+installed version of a module instead of the development version.
+
+It is also possible to analyze python files, with a few
+restriction. The thing to keep in mind is that pylint will try to
+convert the file name to a module name, and only be able to process
+the file if it succeeds. ::
+
+ pylint mymodule.py
+
+should always work since the current working
+directory is automatically added on top of the python path ::
+
+ pylint directory/mymodule.py
+
+will work if "directory" is a python package (i.e. has an __init__.py
+file) or if "directory" is in the python path.
+
+For more details on this see the `Frequently Asked Questions`_.
+
+You can also start a thin gui around pylint (require TkInter) by
+typing ::
+
+ pylint-gui
+
+This should open a window where you can enter the name of the package
+or module to check, at pylint messages will be displayed in the user
+interface.
+
+
+Pylint output
+-------------
+
+The default format for the output is raw text. But passing pylint the
+``--html`` option will produce an HTML document.
+
+There are several sections in pylint's output.
+
+Source code analysis section
+''''''''''''''''''''''''''''
+For each python module,
+pylint will first display a few '*' characters followed by the name
+of the module. Then, a number of messages with the following
+format: ::
+
+ MESSAGE_TYPE: LINE_NUM:[OBJECT:] MESSAGE
+
+You can get another output format, useful since it's recognized by
+most editors or other development tools using the ``--parseable=y``
+option.
+
+The message type can be:
+
+ * [R]efactor for a "good practice" metric violation
+ * [C]onvention for coding standard violation
+ * [W]arning for stylistic problems, or minor programming issues
+ * [E]rror for important programming issues (i.e. most probably bug)
+ * [F]atal for errors which prevented further processing
+
+Sometimes the line of code which caused the error is displayed with
+a caret pointing to the error. This may be generalized in future
+versions of pylint.
+
+Example (extracted from a run of pylint on itself...):
+
+::
+
+ ************* Module pylint.checkers.format
+ W: 50: Too long line (86/80)
+ W:108: Operator not followed by a space
+ print >>sys.stderr, 'Unable to match %r', line
+ ^
+ W:141: Too long line (81/80)
+ W: 74:searchall: Unreachable code
+ W:171:FormatChecker.process_tokens: Redefining built-in (type)
+ W:150:FormatChecker.process_tokens: Too many local variables (20/15)
+ W:150:FormatChecker.process_tokens: Too many branchs (13/12)
+
+
+Reports section
+'''''''''''''''
+Following the analysis message, pylint will display a set of reports,
+each one focusing on a particular aspect of the project, such as number
+of messages by categories, modules dependancies...
+
+For instance, the metrics report displays summaries gathered from the
+current run.
+
+ * the number of processed modules
+ * for each module, the percentage of errors and warnings
+ * the total number of errors and warnings
+ * percentage of classes, functions and modules with docstrings, and
+ a comparison from the previous run
+ * percentage of classes, functions and modules with correct name
+ (according the the coding standard), and a comparison from the
+ previous run
+ * a list of external dependencies found in the code, and where they appear
+
+Also, a global evaluation for the code is computed, and an
+optional witty comment is displayed (if ``--comment=y`` was
+specified on the command line).
+
+
+
+Command line options
+--------------------
+
+First of all, we have two basic (but useful) options.
+
+--version show program's version number and exit
+-h, --help show help about the command line options
+
+Pylint is architectured around several checkers. By default all
+checkers are enabled. You can disable a specific checker by specifying
+``--enable-<checker>=n``, or disable all checkers using
+``--disable-all`` and afterwards enable specific checkers with
+``--enable-<checker>=y``. See the list of available features_ for a
+description of provided checkers with their functionalities.
+
+Each checker has some specific options, which can take either a yes/no
+value, an integer, a python regular expression, or a comma separated
+list of values (which are generally used to override a regular
+expression in special cases). For a full list of options, use ``--help``
+
+Specifying all the options suitable for your setup and coding
+standards can be tedious, so it is possible to use a rc file to
+specify the default values. Pylint looks for /etc/pylintrc and
+~/.pylintrc. The ``--generate-rcfile`` option will generate a
+commented configuration file according to the current configuration on
+standard output and exit. You can put other options before this one to
+use them in the configuration, or start with the default values and
+hand tune the configuration.
+
+Other useful global options include:
+
+--zope Initialize Zope products before starting
+--ignore=file Add <file> (may be a directory) to the black
+ list. It should be a base name, not a path.
+ You may set this option multiple times.
+--statistics=y_or_n Compute statistics on collected data.
+--persistent=y_or_n Pickle collected data for later comparisons.
+--comment=y_or_n Add a comment according to your evaluation note.
+--parseable=y_or_n Use a parseable output format.
+--html=y_or_n Use HTML as output format instead of text.
+--enable-msg=msgids Enable the given messages.
+--disable-msg=msgids Disable the given messages.
+--enable-msg-cat=cats Enable all messages in the given categories.
+--disable-msg-cat=cats Disable all messages in the given categories.
+--errors-only Enable only checkers from the error category.
+
+.. _features: features.html
+
+Daily pylint usage
+------------------
+What pylint says is not to be taken as gospel. While getting as
+few false positives for errors as possible is a goal for us -- and
+python makes it hard enough, it is not the case for warnings.
+
+:Quoting Alexandre:
+ My usage pattern for pylint is to generally run pylint -e quite often to
+ get stupid errors flagged before launching an application (or before
+ comitting). I generally run pylint with all the bells and whistles
+ activated some time before a release, when I want to cleanup the code.
+ And when I do that I simply ignore tons of the false warnings (and I
+ can do that without being driven mad by this dumb program which is not
+ smart enough to understand the dynamicity of Python because I only run
+ it once or twice a week in this mode)
+
+:Quoting Marteen Ter Huurne:
+ In our project we just accepted that we have to make some modifications in our
+ code to please PyLint:
+
+ - stick to more naming conventions (unused variables ending in underscores,
+ mix-in class names ending in "Mixin")
+ - making all abstract methods explicit (rather than just not defining them in
+ the superclass)
+ - for messages which are useful in general, but not in a specific case: add "#
+ pylint: disable-msg=X0123" comments
+ - for PyLint bugs: add "#pylint: disable-msg=X0123" comments
+ - for PyLint limitations: add "#pylint: disable-msg=X0123" comments
+ (for instance Twisted's modules create a lot of definitions dynamically so
+ PyLint does not know about them)
+
+ The effort is worth it, since PyLint helps us a lot in keeping the code clean
+ and finding errors early. Although most errors found by PyLint would also be
+ found by the regression tests, by fixing them before committing, we save time.
+ And our regression tests do not cover all code either, just the most complex
+ parts.
+
+
+Bug reports, feedback
+---------------------
+You think you have found a bug in Pylint? Well, this may be the case
+since Pylint is under development. Please take the time to send a bug
+report to python-projects@logilab.org if you've not found it already reported on
+the `tracker page`_. This mailing list is also a nice place to
+discuss Pylint issues, see below for more information about pylint's related
+lists.
+
+You can check for already reported bugs, planned features on pylint's tracker
+web page: http://www.logilab.org/project/name/pylint
+
+Notice that if you don't find something you have expected in pylint's
+tracker page, it may be on the tracker page of one of its dependancies, namely
+astng and common:
+
+* http://www.logilab.org/project/name/logilab-astng
+* http://www.logilab.org/project/name/logilab-common
+
+.. _`tracker page`: http://www.logilab.org/project/name/pylint
+
+Mailing lists
+-------------
+Use the python-projects@logilab.org mailing list for anything related
+to Pylint. This is in most cases better than sending an email directly
+to the author, since others will benefit from the exchange, and you'll
+be more likely answered by someone subscribed to the list. This is a
+moderated mailing list, so if you're not subscribed email you send will have to
+be validated first before actually being sent on the list.
+
+You can subscribe to this mailing list at
+http://lists.logilab.org/mailman/listinfo/python-projects
+
+Archives are available at
+http://lists.logilab.org/pipermail/python-projects/
+
+If you prefer speaking french instead of english, you can use the
+generic forum-fr@logilab.org mailing list:
+
+* (un)subscribe: http://lists.logilab.org/mailman/listinfo/forum-fr
+* archives: http://lists.logilab.org/pipermail/forum-fr
+
+Notice though that this list has a very low traffic since most pylint related
+discussions are done on the python-projects mailing list.
+
+
+
+Advanced usage
+==============
+
+Base configuration
+------------------
+
+To be written...
+
+Environment
+-----------
+
+To be written...
+
+Messages control
+----------------
+
+An example available from the examples directory::
+
+ """pylint option block-disable-msg"""
+
+ __revision__ = None
+
+ class Foo(object):
+ """block-disable-msg test"""
+
+ def __init__(self):
+ pass
+
+ def meth1(self, arg):
+ """this issues a message"""
+ print self
+
+ def meth2(self, arg):
+ """and this one not"""
+ # pylint: disable-msg=W0613
+ print self\
+ + "foo"
+
+ def meth3(self):
+ """test one line disabling"""
+ # no error
+ print self.bla # pylint: disable-msg=E1101
+ # error
+ print self.blop
+
+ def meth4(self):
+ """test re-enabling"""
+ # pylint: disable-msg=E1101
+ # no error
+ print self.bla
+ print self.blop
+ # pylint: enable-msg=E1101
+ # error
+ print self.blip
+
+ def meth5(self):
+ """test IF sub-block re-enabling"""
+ # pylint: disable-msg=E1101
+ # no error
+ print self.bla
+ if self.blop:
+ # pylint: enable-msg=E1101
+ # error
+ print self.blip
+ else:
+ # no error
+ print self.blip
+ # no error
+ print self.blip
+
+ def meth6(self):
+ """test TRY/EXCEPT sub-block re-enabling"""
+ # pylint: disable-msg=E1101
+ # no error
+ print self.bla
+ try:
+ # pylint: enable-msg=E1101
+ # error
+ print self.blip
+ except UndefinedName: # pylint: disable-msg=E0602
+ # no error
+ print self.blip
+ # no error
+ print self.blip
+
+ def meth7(self):
+ """test one line block opening disabling"""
+ if self.blop: # pylint: disable-msg=E1101
+ # error
+ print self.blip
+ else:
+ # error
+ print self.blip
+ # error
+ print self.blip
+
+
+ def meth8(self):
+ """test late disabling"""
+ # error
+ print self.blip
+ # pylint: disable-msg=E1101
+ # no error
+ print self.bla
+ print self.blop
+
+
+
+About analysis
+==============
+
+Pylint heuristics
+-----------------
+
+To be written...
+
+About astng inference
+---------------------
+
+To be written...
+
+
+
+Enhancing Pylint
+================
+
+Writing your own checker
+------------------------
+You can find some simple examples in the examples
+directory of the distribution (custom.py and custom_raw.py). I'll try to
+quickly explain the essentials here.
+
+First, there are two kinds of checkers :
+* raw checkers, which are analysing each module as a raw file stream
+* ast checkers, which are working on an ast representation of the module
+
+The ast representation used is an extension of the one provided with the
+standard python distribution in the `compiler package`_. The extension
+adds additional information and methods on the tree nodes to ease
+navigation and code introspection.
+
+An AST checker is a visitor, and should implement
+visit_<lowered class name>
+leave_<lowered class name>
+methods for the nodes it's interested in. To get description of the different
+classes used in an ast tree, look at the `compiler.ast documentation`.
+Checkers are ordered by priority. For each module, pylint's engine:
+
+1. give the module source file as a stream to raw checkers
+2. get an ast representation for the module
+3. make a depth first descent of the tree, calling visit_<> on each AST
+ checker when entering a node, and living_<> on the back traversal
+
+Notice that the source code is probably the best source of
+documentation, it should be clear and well documented. Don't hesitate to
+ask for any information on the python-projects mailing list.
+
+.. _`compiler package`: http://python.org/doc/current/lib/module-compiler.html
+.. _`compiler.ast documentation`: http://www.python.org/doc/current/lib/module-compiler.ast.html
+
+
+Contribute !
+------------
+All our software is developped using the mercurial_ version control
+system. This is a very cool distributed vcs and its usage is very similar to
+other ones such as cvs or subversion (though the distributed feature introduced
+some different usage patterns). See mercurial home page for installation on
+your computer and basic usage. Note that it's very easy to send us patches using
+`hg email` command ;).
+
+You can get the in-development pylint source code from our public mercurial_
+repository:
+
+http://www.logilab.org/src/pylint
+
+The same is true for pylint dependancies (if you use pylint code from the
+repository, you should usually use code from the repository as well for astng
+and common):
+
+http://www.logilab.org/src/logilab/astng
+http://www.logilab.org/src/logilab/common
+
+.. _mercurial: http://www.selenic.com/mercurial/
+
+
+
+Other information
+=================
+
+IDE integration
+---------------
+Pylint is integrated in the following editors/IDEs:
+
+* emacs (of course)
+* eric3
+* eclipse (using the pydev_ plugin, see also
+ http://msdl.cs.mcgill.ca/MSDL/people/denis/meetings/pythonDev)
+
+To use pylint from within vim, see
+http://www.gonzo.kiev.ua/projects/pylint.vim
+
+To use pylint from within komodo_, see
+http://mateusz.loskot.net/2006/01/15/running-pylint-from-komodo/
+
+To use pylint from within gedit_, see
+http://live.gnome.org/Gedit/PylintPlugin
+
+.. _pydev: http://pydev.sourceforge.net
+.. _komodo: http://www.activestate.com/Products/Komodo/
+.. _gedit: http://www.gnome.org/projects/gedit/
+
+Some projects using Pylint
+--------------------------
+The following projects are known to use pylint to help develop better
+code:
+
+* OSAF Chandler (http://www.osafoundation.org/)
+* Xen (http://www.xensource.com/)
+* CPS (http://www.nuxeo.org)
+* ERP5 (http://www.erp5.org/)
+* pyxmpp (http://pyxmpp.jabberstudio.org/)
+* mercurial
+* eXe (http://exelearning.org/)
+* PrimaGIS (http://www.primagis.org)
+* python-cdd (http://projetos.ossystems.com.br/python-cdd/)
+* CDSWare (http://cdsware.cern.ch/)
+* ASE (http://dcwww.camp.dtu.dk/campos/ASE/intro.html)
+* RunJob (http://projects.fnal.gov/runjob/)
+* Slugathon (http://slugathon.python-hosting.com/)
+* Topographica (http://topographica.org/Home/index.html) (at least they intend to do so)
+* http://browsershots.org
+* many more...
+
+Also notice that the CheeseCake_ kwalitee reporting tool uses pylint to
+analyze the source code.
+
+.. _CheeseCake: http://cheesecake.sourceforge.net/
+
+
+
+.. include:: FAQ.txt
+
+
+
+
diff --git a/doc/quickstart.txt b/doc/quickstart.txt
new file mode 100644
index 0000000..82de686
--- /dev/null
+++ b/doc/quickstart.txt
@@ -0,0 +1,215 @@
+=================
+Pylint Quickstart
+=================
+
+:Author: Alexandre Fayolle
+:Organization: Logilab
+:Version: $Revision: 1.10 $
+:Date: $Date: 2005-04-15 10:40:17 $
+
+.. contents::
+
+
+This document is meant to get you started with Pylint. It assumes that
+you have installed pylint following the instructions in the README
+document found in the source documentation.
+
+
+What is pylint?
+---------------
+
+Pylint is a tool that checks for errors in python code, tries to
+enforce a coding standard and looks for smelling code . This is
+similar but nevertheless different from what pychecker_ provides,
+especially since pychecker explicitly does not bother with coding
+style. The default coding style used by pylint is close to
+`Guido's style guide`_. For more information about code smells, refer
+to Martin Fowler's `refactoring book`_
+
+Pylint will display a number of errors and warnings as it analyzes the
+code, as well as some statistics about the number of warnings and
+errors found in different files. If you run pylint twice, it will
+display the statistics from the previous run together with the ones
+from the current run, so that you can see if the code has improved or
+not.
+
+Last but not least, the code is given an overall mark, based on the
+number an severity of the warnings and errors. This has proven to
+be very motivating for programmers.
+
+
+Invoking pylint
+---------------
+
+Pylint is meant to be called from the command line. The usage is ::
+
+ pylint [options] module_or_package
+
+You should give pylint the name of a Python package or module. Pylint
+will ``import`` this package or module, so you should pay attention to
+your ``PYTHONPATH``, since it is a common error to analyze an
+installed version of a module instead of the development version.
+
+It is also possible to analyze python files, with a few
+restrictions. The thing to keep in mind is that pylint will try to
+convert the file name to a module name, and only be able to process
+the file if it succeeds. ::
+
+ pylint mymodule.py
+
+should always works since the current working
+directory is automatically added on top of the python path ::
+
+ pylint directory/mymodule.py
+
+will work if "directory" is a python package (i.e. has an __init__.py
+file) or if "directory" is in the python path.
+
+For more details on this see the FAQ_.
+
+You can also start a thin gui around pylint (require TkInter) by
+typing ::
+
+ pylint-gui
+
+This should open a window where you can enter the name of the package
+or module to check, at pylint messages will be displayed in the user
+interface.
+
+
+Pylint output
+-------------
+
+The default format for the output is raw text. But passing pylint the
+``--html`` option will produce an HTML document.
+
+There are several sections in pylint's output.
+
+Source code analysis section
+''''''''''''''''''''''''''''
+
+For each python module,
+pylint will first display a few '*' characters followed by the name
+of the module. Then, a number of messages with the following
+format: ::
+
+ MESSAGE_TYPE: LINE_NUM:[OBJECT:] MESSAGE
+
+You can get another output format, useful since it's recognized by
+most editors or other development tools using the ``--parseable=y``
+option.
+
+The message type can be:
+
+ * [R]efactor for a "good practice" metric violation
+ * [C]onvention for coding standard violation
+ * [W]arning for stylistic problems, or minor programming issues
+ * [E]rror for important programming issues (i.e. most probably bug)
+ * [F]atal for errors which prevented further processing
+
+Sometimes the line of code which caused the error is displayed with
+a caret pointing to the error. This may be generalized in future
+versions of pylint.
+
+Example (extracted from a run of pylint on itself...):
+
+::
+
+ ************* Module pylint.checkers.format
+ W: 50: Too long line (86/80)
+ W:108: Operator not followed by a space
+ print >>sys.stderr, 'Unable to match %r', line
+ ^
+ W:141: Too long line (81/80)
+ W: 74:searchall: Unreachable code
+ W:171:FormatChecker.process_tokens: Redefining built-in (type)
+ W:150:FormatChecker.process_tokens: Too many local variables (20/15)
+ W:150:FormatChecker.process_tokens: Too many branchs (13/12)
+
+
+Reports section
+'''''''''''''''
+
+Following the analysis message, pylint will display a set of reports,
+each one focusing on a particular aspect of the project, such as number
+of messages by categories, modules dependancies...
+
+For instance, the metrics report displays summaries gathered from the
+current run.
+
+ * the number of processed modules
+ * for each module, the percentage of errors and warnings
+ * the total number of errors and warnings
+ * percentage of classes, functions and modules with docstrings, and
+ a comparison from the previous run
+ * percentage of classes, functions and modules with correct name
+ (according the the coding standard), and a comparison from the
+ previous run
+ * a list of external dependencies found in the code, and where they appear
+
+Also, a global evaluation for the code is computed, and an
+optional witty comment is displayed (if ``--comment=y`` was
+specified on the command line).
+
+
+Command line options
+--------------------
+
+First of all, we have two basic (but useful) options.
+
+--version show program's version number and exit
+-h, --help show help about the command line options
+
+Pylint is architectured around several checkers. By default all
+checkers are enabled. You can disable a specific checker by specifying
+``--enable-<checker>=n``, or disable all checkers using
+``--disable-all`` and afterwards enable specific checkers with
+``--enable-<checker>=y``. See the list of available features_ for a
+description of provided checkers with their functionalities.
+
+Each checker has some specific options, which can take either a yes/no
+value, an integer, a python regular expression, or a comma separated
+list of values (which are generally used to override a regular
+expression in special cases). For a full list of options, use ``--help``
+
+Specifying all the options suitable for your setup and coding
+standards can be tedious, so it is possible to use a rc file to
+specify the default values. Pylint looks for /etc/pylintrc and
+~/.pylintrc. The ``--generate-rcfile`` option will generate a
+commented configuration file according to the current configuration on
+standard output and exit. You can put other options before this one to
+use them in the configuration, or start with the default values and
+hand tune the configuration.
+
+Other useful global options include:
+
+--zope Initialize Zope products before starting
+--ignore=file Add <file> (may be a directory) to the black
+ list. It should be a base name, not a path.
+ You may set this option multiple times.
+--statistics=y_or_n Compute statistics on collected data.
+--persistent=y_or_n Pickle collected data for later comparisons.
+--comment=y_or_n Add a comment according to your evaluation note.
+--parseable=y_or_n Use a parseable output format.
+--html=y_or_n Use HTML as output format instead of text.
+--enable-msg=msgids Enable the given messages.
+--disable-msg=msgids Disable the given messages.
+--enable-msg-cat=cats Enable all messages in the given categories.
+--disable-msg-cat=cats Disable all messages in the given categories.
+
+
+
+Bug reports
+-----------
+
+You think you have found a bug in Pylint? Well, this may be the case
+since Pylint is under development. Please take the time to send a bug
+report to python-projects@logilab.org. This mailing list is also a
+nice place to discuss Pylint issues.
+
+
+.. _pychecker: http://pychecker.sf.net
+.. _features: features.html
+.. _FAQ: FAQ.html
+.. _`Guido's style guide`: http://www.python.org/doc/essays/styleguide.html
+.. _`refactoring book`: http://www.refactoring.com/
diff --git a/doc/rpython.txt b/doc/rpython.txt
new file mode 100644
index 0000000..218f854
--- /dev/null
+++ b/doc/rpython.txt
@@ -0,0 +1,30 @@
+Since 0.17, pylint contains a Restricted python checker (rpython).
+It contains the following checks:
+
+* unavailable keywords / builtins
+* multiple inheritance
+* mixing multiple types
+* non homogeneous list
+* global modification
+* negative slice index
+* using %r in format string
+* warn about special methods that are not implicitly called
+
+By default the rpython checker is deactivated. Activate it using :
+
+ pylint --rpython-mode -rn ...
+
+(-rn is disabling statistics reports) or
+
+ pylint --enable-checker=rpython ...
+
+to get only rpython checks (though in this case you won't be warned about
+regular errors).
+
+Another interesting thing is the rpython dedicated testing framework,
+testing that checked things are actually not translatable. I have the idea
+that this may be useful to generate some kind of documentation for
+features supported by rpython or not, and help spread information when a
+feature that wasn't supported is introduced in rpython. That's another
+story though... If you're interested, check
+pylint/test/test_rpycompilation.py.
diff --git a/elisp/pylint-flymake.el b/elisp/pylint-flymake.el
new file mode 100644
index 0000000..835409e
--- /dev/null
+++ b/elisp/pylint-flymake.el
@@ -0,0 +1,11 @@
+(when (load "flymake" t)
+ (defun flymake-pylint-init ()
+ (let* ((temp-file (flymake-init-create-temp-buffer-copy
+ 'flymake-create-temp-inplace))
+ (local-file (file-relative-name
+ temp-file
+ (file-name-directory buffer-file-name))))
+ (list "epylint" (list local-file))))
+
+ (add-to-list 'flymake-allowed-file-name-masks
+ '("\\.py\\'" flymake-pylint-init)))
diff --git a/elisp/pylint.el b/elisp/pylint.el
new file mode 100644
index 0000000..5862a2e
--- /dev/null
+++ b/elisp/pylint.el
@@ -0,0 +1,60 @@
+(require 'compile)
+
+;;
+;; Modifications done by Yarosav O. Halchenko (2008):
+;; - enable user-visible variables
+;; distributed under the same copyright/license terms as
+;; pylint itself
+;;
+(require 'compile)
+
+;; user definable variables
+;; vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
+
+(defgroup pylint nil
+ "Emacs support for the Pylint Python checker"
+ :group 'languages
+ :prefix "pylint-")
+
+(defcustom pylint-options "--output-format=parseable"
+ "*Command line options to be used with pylint call"
+ :type 'string
+ :group 'pylint)
+
+
+;; adapted from pychecker for pylint
+(defun pylint-python-hook ()
+ (defun pylint ()
+ "Run pylint against the file behind the current buffer after
+ checking if unsaved buffers should be saved."
+
+ (interactive)
+ (let* ((file (buffer-file-name (current-buffer)))
+ (command (concat "pylint " pylint-options " \"" file "\"")))
+ (save-some-buffers (not compilation-ask-about-save) nil) ; save files.
+ (compile-internal command "No more errors or warnings" "pylint")))
+;; (local-set-key [f1] 'pylint)
+;; (local-set-key [f2] 'previous-error)
+;; (local-set-key [f3] 'next-error)
+
+ (define-key
+ py-mode-map
+ [menu-bar Python pylint-separator]
+ '("--" . pylint-seperator))
+
+ (define-key
+ py-mode-map
+ [menu-bar Python next-error]
+ '("Next error" . next-error))
+ (define-key
+ py-mode-map
+ [menu-bar Python prev-error]
+ '("Previous error" . previous-error))
+ (define-key
+ py-mode-map
+ [menu-bar Python lint]
+ '("Pylint" . pylint))
+
+ )
+
+(add-hook 'python-mode-hook 'pylint-python-hook)
diff --git a/elisp/startup b/elisp/startup
new file mode 100644
index 0000000..fd40b42
--- /dev/null
+++ b/elisp/startup
@@ -0,0 +1,17 @@
+;; -*-emacs-lisp-*-
+;;
+;; Emacs startup file for the Debian GNU/Linux %PACKAGE% package
+;;
+;; Originally contributed by Nils Naumann <naumann@unileoben.ac.at>
+;; Modified by Dirk Eddelbuettel <edd@debian.org>
+;; Adapted for dh-make by Jim Van Zandt <jrv@vanzandt.mv.com>
+
+;; The %PACKAGE% package follows the Debian/GNU Linux 'emacsen' policy and
+;; byte-compiles its elisp files for each 'emacs flavor' (emacs19,
+;; xemacs19, emacs20, xemacs20...). The compiled code is then
+;; installed in a subdirectory of the respective site-lisp directory.
+;; We have to add this to the load-path:
+(setq load-path (cons (concat "/usr/share/"
+ (symbol-name debian-emacs-flavor)
+ "/site-lisp/%PACKAGE%") load-path))
+(load-library "pylint")
diff --git a/examples/custom.py b/examples/custom.py
new file mode 100644
index 0000000..73b64e1
--- /dev/null
+++ b/examples/custom.py
@@ -0,0 +1,38 @@
+from logilab import astng
+
+from pylint.interfaces import IASTNGChecker
+from pylint.checkers import BaseChecker
+
+class MyASTNGChecker(BaseChecker):
+ """add member attributes defined using my own "properties" function
+ to the class locals dictionary
+ """
+
+ __implements__ = IASTNGChecker
+
+ name = 'custom'
+ msgs = {}
+ options = ()
+ # this is important so that your checker is executed before others
+ priority = -1
+
+ def visit_callfunc(self, node):
+ """called when a CallFunc node is encountered. See compiler.ast
+ documentation for a description of available nodes:
+ http://www.python.org/doc/current/lib/module-compiler.ast.html
+ )
+ """
+ if not (isinstance(node.node, astng.Getattr)
+ and isinstance(node.node.expr, astng.Name)
+ and node.node.expr.name == 'properties'
+ and node.node.attrname == 'create'):
+ return
+ in_class = node.frame()
+ for param in node.args:
+ in_class.locals[param.name] = node
+
+
+def register(linter):
+ """required method to auto register this checker"""
+ linter.register_checker(MyASTNGChecker(linter))
+
diff --git a/examples/custom_raw.py b/examples/custom_raw.py
new file mode 100644
index 0000000..701f6e9
--- /dev/null
+++ b/examples/custom_raw.py
@@ -0,0 +1,31 @@
+from pylint.interfaces import IRawChecker
+from pylint.checkers import BaseChecker
+
+class MyRawChecker(BaseChecker):
+ """check for line continuations with '\' instead of using triple
+ quoted string or parenthesis
+ """
+
+ __implements__ = IRawChecker
+
+ name = 'custom_raw'
+ msgs = {'W9901': ('use \\ for line continuation',
+ ('Used when a \\ is used for a line continuation instead'
+ ' of using triple quoted string or parenthesis.')),
+ }
+ options = ()
+
+ def process_module(self, stream):
+ """process a module
+
+ the module's content is accessible via the stream object
+ """
+ for (lineno, line) in enumerate(stream):
+ if line.rstrip().endswith('\\'):
+ self.add_message('W9901', line=lineno)
+
+
+def register(linter):
+ """required method to auto register this checker"""
+ linter.register_checker(MyRawChecker(linter))
+
diff --git a/examples/pylintrc b/examples/pylintrc
new file mode 100644
index 0000000..f674e19
--- /dev/null
+++ b/examples/pylintrc
@@ -0,0 +1,309 @@
+# 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 astng checker in order
+# to:
+# * handle message activation / deactivation at the module level
+# * handle some basic but necessary stats'data (number of classes, methods...)
+#
+[MASTER]
+
+# Specify a configuration file.
+#rcfile=
+
+# Python code to execute, usually for sys.path manipulation such as
+# pygtk.require().
+#init-hook=
+
+# Profiled execution.
+profile=no
+
+# Add <file or directory> to the black list. It should be a base name, not a
+# path. You may set this option multiple times.
+ignore=CVS
+
+# Pickle collected data for later comparisons.
+persistent=yes
+
+# Set the cache size for astng objects.
+cache-size=500
+
+# List of plugins (as comma separated values of python modules names) to load,
+# usually to register additional checkers.
+load-plugins=
+
+
+[MESSAGES CONTROL]
+
+# Enable only checker(s) with the given id(s). This option conflicts with the
+# disable-checker option
+#enable-checker=
+
+# Enable all checker(s) except those with the given id(s). This option
+# conflicts with the enable-checker option
+#disable-checker=
+
+# Enable all messages in the listed categories.
+#enable-msg-cat=
+
+# Disable all messages in the listed categories.
+#disable-msg-cat=
+
+# Enable the message(s) with the given id(s).
+#enable-msg=
+
+# Disable the message(s) with the given id(s).
+#disable-msg=
+
+
+[REPORTS]
+
+# Set the output format. Available formats are text, parseable, colorized, msvs
+# (visual studio) and html
+output-format=text
+
+# Include message's id in output
+include-ids=no
+
+# 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]".
+files-output=no
+
+# Tells wether to display a full report or only the messages
+reports=yes
+
+# 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
+# respectivly contain the number of errors / warnings messages and the total
+# number of statements analyzed. This is used by the global evaluation report
+# (R0004).
+evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)
+
+# Add a comment according to your evaluation note. This is used by the global
+# evaluation report (R0004).
+comment=no
+
+# Enable the report(s) with the given id(s).
+#enable-report=
+
+# Disable the report(s) with the given id(s).
+#disable-report=
+
+
+# checks for :
+# * doc strings
+# * modules / classes / functions / methods / arguments / variables name
+# * number of arguments, local variables, branchs, returns and statements in
+# functions, methods
+# * required module attributes
+# * dangerous default values as arguments
+# * redefinition of function / method / class
+# * uses of the global statement
+#
+[BASIC]
+
+# Required attributes for module, separated by a comma
+required-attributes=
+
+# Regular expression which should only match functions or classes name which do
+# not require a docstring
+no-docstring-rgx=__.*__
+
+# Regular expression which should only match correct module names
+module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$
+
+# Regular expression which should only match correct module level names
+const-rgx=(([A-Z_][A-Z1-9_]*)|(__.*__))$
+
+# Regular expression which should only match correct class names
+class-rgx=[A-Z_][a-zA-Z0-9]+$
+
+# Regular expression which should only match correct function names
+function-rgx=[a-z_][a-z0-9_]{2,30}$
+
+# Regular expression which should only match correct method names
+method-rgx=[a-z_][a-z0-9_]{2,30}$
+
+# Regular expression which should only match correct instance attribute names
+attr-rgx=[a-z_][a-z0-9_]{2,30}$
+
+# Regular expression which should only match correct argument names
+argument-rgx=[a-z_][a-z0-9_]{2,30}$
+
+# Regular expression which should only match correct variable names
+variable-rgx=[a-z_][a-z0-9_]{2,30}$
+
+# Regular expression which should only match correct list comprehension /
+# generator expression variable names
+inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$
+
+# Good variable names which should always be accepted, separated by a comma
+good-names=i,j,k,ex,Run,_
+
+# Bad variable names which should always be refused, separated by a comma
+bad-names=foo,bar,baz,toto,tutu,tata
+
+# List of builtins function names that should not be used, separated by a comma
+bad-functions=map,filter,apply,input
+
+
+# try to find bugs in the code using type inference
+#
+[TYPECHECK]
+
+# Tells wether missing members accessed in mixin class should be ignored. A
+# mixin class is detected if its name ends with "mixin" (case insensitive).
+ignore-mixin-members=yes
+
+# List of classes names for which member attributes should not be checked
+# (useful for classes with attributes dynamicaly set).
+ignored-classes=SQLObject
+
+# When zope mode is activated, consider the acquired-members option to ignore
+# access to some undefined attributes.
+zope=no
+
+# List of members which are usually get through zope's acquisition mecanism and
+# so shouldn't trigger E0201 when accessed (need zope=yes to be considered).
+acquired-members=REQUEST,acl_users,aq_parent
+
+
+# checks for
+# * unused variables / imports
+# * undefined variables
+# * redefinition of variable from builtins or from an outer scope
+# * use of variable before assigment
+#
+[VARIABLES]
+
+# Tells wether we should check for unused import in __init__ files.
+init-import=no
+
+# A regular expression matching names used for dummy variables (i.e. not used).
+dummy-variables-rgx=_|dummy
+
+# List of additional names supposed to be defined in builtins. Remember that
+# you should avoid to define new builtins when possible.
+additional-builtins=
+
+
+# checks for :
+# * methods without self as first argument
+# * overridden methods signature
+# * access only to existant members via self
+# * attributes not defined in the __init__ method
+# * supported interfaces implementation
+# * unreachable code
+#
+[CLASSES]
+
+# 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.
+ignore-iface-methods=isImplementedBy,deferred,extends,names,namesAndDescriptions,queryDescriptionFor,getBases,getDescriptionFor,getDoc,getName,getTaggedValue,getTaggedValueTags,isEqualOrExtendedBy,setTaggedValue,isImplementedByInstancesOf,adaptWith,is_implemented_by
+
+# List of method names used to declare (i.e. assign) instance attributes.
+defining-attr-methods=__init__,__new__,setUp
+
+
+# checks for sign of poor/misdesign:
+# * number of methods, attributes, local variables...
+# * size, complexity of functions, methods
+#
+[DESIGN]
+
+# Maximum number of arguments for function / method
+max-args=5
+
+# Maximum number of locals for function / method body
+max-locals=15
+
+# Maximum number of return / yield for function / method body
+max-returns=6
+
+# Maximum number of branch for function / method body
+max-branchs=12
+
+# Maximum number of statements in function / method body
+max-statements=50
+
+# Maximum number of parents for a class (see R0901).
+max-parents=7
+
+# Maximum number of attributes for a class (see R0902).
+max-attributes=7
+
+# Minimum number of public methods for a class (see R0903).
+min-public-methods=2
+
+# Maximum number of public methods for a class (see R0904).
+max-public-methods=20
+
+
+# checks for
+# * external modules dependencies
+# * relative / wildcard imports
+# * cyclic imports
+# * uses of deprecated modules
+#
+[IMPORTS]
+
+# Deprecated modules which should not be used, separated by a comma
+deprecated-modules=regsub,string,TERMIOS,Bastion,rexec
+
+# Create a graph of every (i.e. internal and external) dependencies in the
+# given file (report R0402 must not be disabled)
+import-graph=
+
+# Create a graph of external dependencies in the given file (report R0402 must
+# not be disabled)
+ext-import-graph=
+
+# Create a graph of internal dependencies in the given file (report R0402 must
+# not be disabled)
+int-import-graph=
+
+
+# checks for :
+# * unauthorized constructions
+# * strict indentation
+# * line length
+# * use of <> instead of !=
+#
+[FORMAT]
+
+# Maximum number of characters on a single line.
+max-line-length=80
+
+# Maximum number of lines in a module
+max-module-lines=1000
+
+# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1
+# tab).
+indent-string=' '
+
+
+# checks for:
+# * warning notes in the code like FIXME, XXX
+# * PEP 263: source code with non ascii character but no encoding declaration
+#
+[MISCELLANEOUS]
+
+# List of note tags to take in consideration, separated by a comma.
+notes=FIXME,XXX,TODO
+
+
+# checks for similarities and duplicated code. This computation may be
+# memory / CPU intensive, so you should disable it if you experiments some
+# problems.
+#
+[SIMILARITIES]
+
+# Minimum lines number of a similarity.
+min-similarity-lines=4
+
+# Ignore comments when computing similarities.
+ignore-comments=yes
+
+# Ignore docstrings when computing similarities.
+ignore-docstrings=yes
diff --git a/examples/pylintrc_camelcase b/examples/pylintrc_camelcase
new file mode 100644
index 0000000..0dd9266
--- /dev/null
+++ b/examples/pylintrc_camelcase
@@ -0,0 +1,24 @@
+# This pylintrc file will use the default settings except for the
+# naming conventions, which will allow for camel case naming as found
+# in Java code or several libraries such as PyQt, etc.
+
+[BASIC]
+# Regular expression which should only match correct module names
+module-rgx=(([a-z][a-z0-9]*)|([A-Z][a-zA-Z0-9]+))$
+
+# Regular expression which should only match correct class names
+class-rgx=[A-Z][a-zA-Z0-9]+$
+
+# Regular expression which should only match correct function names
+function-rgx=[a-z_][a-zA-Z0-9]*$
+
+# Regular expression which should only match correct method names
+method-rgx=[a-z_][a-zA-Z0-9]*$
+
+# Regular expression which should only match correct argument names
+argument-rgx=[a-z][a-zA-Z0-9]*$
+
+# Regular expression which should only match correct variable names
+variable-rgx=[a-z][a-zA-Z0-9]*$
+
+
diff --git a/gui.py b/gui.py
new file mode 100644
index 0000000..3c50120
--- /dev/null
+++ b/gui.py
@@ -0,0 +1,82 @@
+"""Tkinker gui for pylint"""
+
+from Tkinter import Tk, Frame, Listbox, Entry, Label, Button, Scrollbar
+from Tkinter import TOP, LEFT, RIGHT, BOTTOM, END, X, Y, BOTH
+import os
+import sys
+
+if sys.platform.startswith('win'):
+ PYLINT = 'pylint.bat'
+else:
+ PYLINT = 'pylint'
+
+class LintGui:
+ """Build and control a window to interact with pylint"""
+
+ def __init__(self, root=None):
+ self.root = root or Tk()
+ self.root.title('Pylint')
+ top_frame = Frame(self.root)
+ res_frame = Frame(self.root)
+ btn_frame = Frame(self.root)
+ top_frame.pack(side=TOP, fill=X)
+ res_frame.pack(side=TOP, fill=BOTH, expand=True)
+ btn_frame.pack(side=TOP, fill=X)
+
+ Label(top_frame, text='Module or package').pack(side=LEFT)
+ self.txtModule = Entry(top_frame, background='white')
+ self.txtModule.bind('<Return>', self.run_lint)
+ self.txtModule.pack(side=LEFT, expand=True, fill=X)
+ Button(top_frame, text='Run', command=self.run_lint).pack(side=LEFT)
+
+ scrl = Scrollbar(res_frame)
+ self.results = Listbox(res_frame,
+ background='white',
+ font='fixedsys',
+ selectmode='browse',
+ yscrollcommand=scrl.set)
+ scrl.configure(command=self.results.yview)
+ self.results.pack(side=LEFT, expand=True, fill=BOTH)
+ scrl.pack(side=RIGHT, fill=Y)
+
+ Button(btn_frame, text='Quit', command=self.quit).pack(side=BOTTOM)
+ #self.root.bind('<ctrl-q>', self.quit)
+ self.txtModule.focus_set()
+
+ def mainloop(self):
+ """lauch the mainloop of the application"""
+ self.root.mainloop()
+
+ def quit(self, _=None):
+ """quit the application"""
+ self.root.quit()
+
+ def run_lint(self, _=None):
+ """lauches pylint"""
+ colors = {'W:':'red1', 'E:': 'red4',
+ 'W:': 'red3', '**': 'navy'}
+
+ self.root.configure(cursor='watch')
+ self.results.focus_set()
+ self.results.delete(0, END)
+ self.results.update()
+ module = self.txtModule.get()
+ pout = os.popen('%s %s' % (PYLINT, module), 'r')
+ for line in pout.xreadlines():
+ line = line.rstrip()
+ self.results.insert(END, line)
+ fg_color = colors.get(line[:2], 'black')
+ self.results.itemconfigure(END, fg=fg_color)
+ self.results.update()
+ self.root.configure(cursor='')
+
+def Run(args):
+ """launch pylint gui from args"""
+ if args:
+ print 'USAGE: pylint-gui\n launch a simple pylint gui using Tk'
+ return
+ gui = LintGui()
+ gui.mainloop()
+
+if __name__ == '__main__':
+ Run(sys.argv[1:])
diff --git a/interfaces.py b/interfaces.py
new file mode 100644
index 0000000..9771aaf
--- /dev/null
+++ b/interfaces.py
@@ -0,0 +1,98 @@
+# 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.,
+# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+""" Copyright (c) 2002-2003 LOGILAB S.A. (Paris, FRANCE).
+ http://www.logilab.fr/ -- mailto:contact@logilab.fr
+
+Interfaces for PyLint objects
+"""
+
+__revision__ = "$Id: interfaces.py,v 1.9 2004-04-24 12:14:53 syt Exp $"
+
+from logilab.common.interface import Interface
+
+
+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)"""
+
+## def open_module(self):
+## """called before visiting a module"""
+
+## def close_module(self):
+## """called after visiting a module"""
+
+
+class IRawChecker(IChecker):
+ """interface for checker which need to parse the raw file
+ """
+
+ def process_module(self, stream):
+ """ process a module
+
+ the module's content is accessible via the stream object
+ """
+
+
+class IASTNGChecker(IChecker):
+ """ interface for checker which prefers receive events according to
+ statement type
+ """
+
+
+class ILinter(Interface):
+ """interface for the linter class
+
+ the linter class will generate events to its registered checkers.
+ Each ckecker may interact with the linter instance using this API
+ """
+
+ def register_checker(self, checker):
+ """register a new checker class
+
+ checker is a class implementing IrawChecker or / and IASTNGChecker
+ """
+
+ def add_message(self, msg_id, line=None, node=None, args=None):
+ """add the message corresponding to the given id.
+
+ If provided, msg is expanded using args
+
+ astng checkers should provide the node argument,
+ raw checkers should provide the line argument.
+ """
+
+
+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', 'IStatable', 'ILinter', 'IReporter')
diff --git a/lint.py b/lint.py
new file mode 100644
index 0000000..abe7411
--- /dev/null
+++ b/lint.py
@@ -0,0 +1,957 @@
+# Copyright (c) 2003-2008 Sylvain Thenault (thenault@gmail.com).
+# Copyright (c) 2003-2008 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.,
+# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+""" %prog [options] module_or_package
+
+ Check that a module satisfy a coding standard (and more !).
+
+ %prog --help
+
+ Display this help message and exit.
+
+ %prog --help-msg <msg-id>[,<msg-id>]
+
+ Display help messages about given message identifiers and exit.
+"""
+
+# import this first to avoid further builtins pollution possibilities
+from pylint.checkers import utils
+
+import sys
+import os
+import re
+import tokenize
+from os.path import dirname, basename, splitext, exists, isdir, join, normpath
+
+from logilab.common.configuration import OptionsManagerMixIn, check_csv
+from logilab.common.modutils import modpath_from_file, get_module_files, \
+ file_from_modpath, load_module_from_name
+from logilab.common.interface import implements
+from logilab.common.textutils import get_csv
+from logilab.common.fileutils import norm_open
+from logilab.common.ureports import Table, Text
+from logilab.common.__pkginfo__ import version as common_version
+
+from logilab.astng import ASTNGManager
+from logilab.astng.__pkginfo__ import version as astng_version
+
+from pylint.utils import UnknownMessage, MessagesHandlerMixIn, \
+ ReportsHandlerMixIn, MSG_TYPES, sort_checkers
+from pylint.interfaces import ILinter, IRawChecker, IASTNGChecker
+from pylint.checkers import BaseRawChecker, EmptyReport, \
+ table_lines_from_stats
+from pylint.reporters.text import TextReporter, ParseableTextReporter, \
+ VSTextReporter, ColorizedTextReporter
+from pylint.reporters.html import HTMLReporter
+from pylint import config
+
+from pylint.__pkginfo__ import version
+
+
+OPTION_RGX = re.compile('\s*#*\s*pylint:(.*)')
+REPORTER_OPT_MAP = {'text': TextReporter,
+ 'parseable': ParseableTextReporter,
+ 'msvs': VSTextReporter,
+ 'colorized': ColorizedTextReporter,
+ 'html': HTMLReporter,}
+
+# Python Linter class #########################################################
+
+MSGS = {
+ 'F0001': ('%s',
+ 'Used when an error occured preventing the analysis of a \
+ module (unable to find it for instance).'),
+ 'F0002': ('%s: %s',
+ 'Used when an unexpected error occured while building the ASTNG \
+ representation. This is usually accompanied by a traceback. \
+ Please report such errors !'),
+ 'F0003': ('ignored builtin module %s',
+ 'Used to indicate that the user asked to analyze a builtin module\
+ which has been skipped.'),
+ 'F0004': ('unexpected infered value %s',
+ 'Used to indicate that some value of an unexpected type has been \
+ infered.'),
+
+ 'I0001': ('Unable to run raw checkers on built-in module %s',
+ 'Used to inform that a built-in module has not been checked \
+ using the raw checkers.'),
+
+ 'I0010': ('Unable to consider inline option %r',
+ 'Used when an inline option is either badly formatted or can\'t \
+ be used inside modules.'),
+
+ 'I0011': ('Locally disabling %s',
+ 'Used when an inline option disables a message or a messages \
+ category.'),
+ 'I0012': ('Locally enabling %s',
+ 'Used when an inline option enables a message or a messages \
+ category.'),
+ 'I0013': ('Ignoring entire file',
+ 'Used to inform that the file will not be checked'),
+
+
+ 'E0001': ('%s',
+ 'Used when a syntax error is raised for a module.'),
+
+ 'E0011': ('Unrecognized file option %r',
+ 'Used when an unknown inline option is encountered.'),
+ 'E0012': ('Bad option value %r',
+ 'Used when a bad value for an inline option is encountered.'),
+ }
+
+class PyLinter(OptionsManagerMixIn, MessagesHandlerMixIn, ReportsHandlerMixIn,
+ BaseRawChecker):
+ """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 astng checker in order
+ to:
+ * handle message activation / deactivation at the module level
+ * handle some basic but necessary stats'data (number of classes, methods...)
+ """
+
+ __implements__ = (ILinter, IRawChecker, IASTNGChecker)
+
+ name = 'master'
+ priority = 0
+ msgs = MSGS
+ may_be_disabled = False
+
+ options = (('ignore',
+ {'type' : 'csv', 'metavar' : '<file>',
+ 'dest' : 'black_list', 'default' : ('CVS',),
+ 'help' : 'Add <file or directory> to the black list. It \
+should be a base name, not a path. You may set this option multiple times.'}),
+
+ ('enable-checker',
+ {'type' : 'csv', 'metavar': '<checker ids>',
+ 'group': 'Messages control',
+ 'help' : 'Enable only checker(s) with the given id(s).\
+ This option conflicts with the disable-checker option'}),
+
+ ('disable-checker',
+ {'type' : 'csv', 'metavar': '<checker ids>',
+ 'group': 'Messages control',
+ 'help' : 'Enable all checker(s) except those with the \
+ given id(s).\
+ This option conflicts with the enable-checker option'}),
+
+ ('persistent',
+ {'default': True, 'type' : 'yn', 'metavar' : '<y_or_n>',
+ 'help' : 'Pickle collected data for later comparisons.'}),
+
+ ('cache-size',
+ {'default': 500, 'type' : 'int', 'metavar': '<size>',
+ 'help' : 'Set the cache size for astng objects.'}),
+
+ ('load-plugins',
+ {'type' : 'csv', 'metavar' : '<modules>', 'default' : (),
+ 'help' : 'List of plugins (as comma separated values of \
+python modules names) to load, usually to register additional checkers.'}),
+
+ ('output-format',
+ {'default': 'text', 'type': 'choice', 'metavar' : '<format>',
+ 'choices': ('text', 'parseable', 'msvs', 'colorized', 'html'),
+ 'short': 'f',
+ 'group': 'Reports',
+ 'help' : 'Set the output format. Available formats are text,\
+ parseable, colorized, msvs (visual studio) and html'}),
+
+ ('include-ids',
+ {'type' : 'yn', 'metavar' : '<y_or_n>', 'default' : 0,
+ 'short': 'i',
+ 'group': 'Reports',
+ 'help' : 'Include message\'s id in output'}),
+
+ ('files-output',
+ {'default': 0, 'type' : 'yn', 'metavar' : '<y_or_n>',
+ 'group': 'Reports',
+ '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' : '<y_or_n>',
+ 'short': 'r',
+ 'group': 'Reports',
+ 'help' : 'Tells wether to display a full report or only the\
+ messages'}),
+
+ ('evaluation',
+ {'type' : 'string', 'metavar' : '<python_expression>',
+ 'group': 'Reports',
+ '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 respectivly contain the number of errors / warnings\
+ messages and the total number of statements analyzed. This is used by the \
+ global evaluation report (R0004).'}),
+
+ ('comment',
+ {'default': 0, 'type' : 'yn', 'metavar' : '<y_or_n>',
+ 'group': 'Reports',
+ 'help' : 'Add a comment according to your evaluation note. \
+This is used by the global evaluation report (R0004).'}),
+
+ ('enable-report',
+ {'type' : 'csv', 'metavar': '<rpt ids>',
+ 'group': 'Reports',
+ 'help' : 'Enable the report(s) with the given id(s).'}),
+
+ ('disable-report',
+ {'type' : 'csv', 'metavar': '<rpt ids>',
+ 'group': 'Reports',
+ 'help' : 'Disable the report(s) with the given id(s).'}),
+
+ ('enable-msg-cat',
+ {'type' : 'csv', 'metavar': '<msg cats>',
+ 'group': 'Messages control',
+ 'help' : 'Enable all messages in the listed categories.'}),
+
+ ('disable-msg-cat',
+ {'type' : 'csv', 'metavar': '<msg cats>',
+ 'group': 'Messages control',
+ 'help' : 'Disable all messages in the listed categories.'}),
+
+ ('enable-msg',
+ {'type' : 'csv', 'metavar': '<msg ids>',
+ 'group': 'Messages control',
+ 'help' : 'Enable the message(s) with the given id(s).'}),
+
+ ('disable-msg',
+ {'type' : 'csv', 'metavar': '<msg ids>',
+ 'group': 'Messages control',
+ 'help' : 'Disable the message(s) with the given id(s).'}),
+ )
+ option_groups = (
+ ('Messages control', 'Options controling analysis messages'),
+ ('Reports', 'Options related to output formating and reporting'),
+ )
+
+ def __init__(self, options=(), reporter=None, option_groups=(),
+ pylintrc=None):
+ # some stuff has to be done before ancestors initialization...
+ #
+ # checkers / reporter / astng manager
+ self.reporter = None
+ self.manager = ASTNGManager()
+ self._checkers = {}
+ self._ignore_file = False
+ # visit variables
+ self.base_name = None
+ self.base_file = None
+ self.current_name = None
+ self.current_file = None
+ self.stats = None
+ # init options
+ self.options = options + PyLinter.options
+ self.option_groups = option_groups + PyLinter.option_groups
+ self._options_methods = {
+ 'enable-report': self.enable_report,
+ 'disable-report': self.disable_report,
+ 'enable-msg': self.enable_message,
+ 'disable-msg': self.disable_message,
+ 'enable-msg-cat': self.enable_message_category,
+ 'disable-msg-cat': self.disable_message_category}
+ full_version = '%%prog %s, \nastng %s, common %s\nPython %s' % (
+ version, astng_version, common_version, sys.version)
+ OptionsManagerMixIn.__init__(self, usage=__doc__,
+ version=full_version,
+ config_file=pylintrc or config.PYLINTRC)
+ MessagesHandlerMixIn.__init__(self)
+ ReportsHandlerMixIn.__init__(self)
+ BaseRawChecker.__init__(self)
+ # provided reports
+ self.reports = (('R0001', 'Messages by category',
+ report_total_messages_stats),
+ ('R0002', '% errors / warnings by module',
+ report_messages_by_module_stats),
+ ('R0003', 'Messages',
+ report_messages_stats),
+ ('R0004', 'Global evaluation',
+ self.report_evaluation),
+ )
+ self.register_checker(self)
+ self._dynamic_plugins = []
+ self.load_provider_defaults()
+ self.set_reporter(reporter or TextReporter(sys.stdout))
+
+ def load_plugin_modules(self, modnames):
+ """take a list of module names which are pylint plugins and load
+ and register them
+ """
+ for modname in modnames:
+ if modname in self._dynamic_plugins:
+ continue
+ self._dynamic_plugins.append(modname)
+ module = load_module_from_name(modname)
+ module.register(self)
+
+ def set_reporter(self, reporter):
+ """set the reporter used to display messages and reports"""
+ self.reporter = reporter
+ reporter.linter = self
+
+ def set_option(self, opt_name, value, action=None, opt_dict=None):
+ """overridden from configuration.OptionsProviderMixin to handle some
+ special options
+ """
+ if opt_name in self._options_methods:
+ if value:
+ meth = self._options_methods[opt_name]
+ value = check_csv(None, opt_name, value)
+ if isinstance(value, (list, tuple)):
+ for _id in value :
+ meth(_id)
+ else :
+ meth(value)
+ elif opt_name == 'cache-size':
+ self.manager.set_cache_size(int(value))
+ elif opt_name == 'output-format':
+ self.set_reporter(REPORTER_OPT_MAP[value.lower()]())
+ elif opt_name in ('enable-checker', 'disable-checker'):
+ if not value:
+ return
+ checkerids = [v.lower() for v in check_csv(None, opt_name, value)]
+ self.enable_checkers(checkerids, opt_name == 'enable-checker')
+ BaseRawChecker.set_option(self, opt_name, value, action, opt_dict)
+
+ # checkers manipulation methods ###########################################
+
+ def register_checker(self, checker):
+ """register a new checker
+
+ checker is an object implementing IRawChecker or / and IASTNGChecker
+ """
+ assert checker.priority <= 0, 'checker priority can\'t be >= 0'
+ self._checkers[checker.name] = checker
+ if hasattr(checker, 'reports'):
+ for r_id, r_title, r_cb in checker.reports:
+ self.register_report(r_id, r_title, r_cb, checker)
+ self.register_options_provider(checker)
+ if hasattr(checker, 'msgs'):
+ self.register_messages(checker)
+ checker.load_defaults()
+
+ def enable_checkers(self, listed, enabled):
+ """only enable/disable checkers from the given list"""
+ if enabled: # if we are activating a checker; deactivate them all first
+ for checker in self._checkers.values():
+ if not checker.may_be_disabled:
+ continue
+ checker.enable(not enabled)
+ for checkerid in listed:
+ try:
+ checker = self._checkers[checkerid]
+ except KeyError:
+ raise Exception('no checker named %s' % checkerid)
+ checker.enable(enabled)
+
+ def disable_noerror_checkers(self):
+ """disable all checkers without error messages, and the
+ 'miscellaneous' checker which can be safely deactivated in debug
+ mode
+ """
+ for checker in self._checkers.values():
+ if checker.name == 'miscellaneous':
+ checker.enable(False)
+ continue
+ # if checker is already explicitly disabled (e.g. rpython), don't
+ # enable it
+ if checker.enabled:
+ for msgid in getattr(checker, 'msgs', {}).keys():
+ if msgid[0] == 'E':
+ checker.enable(True)
+ break
+ else:
+ checker.enable(False)
+
+ # block level option handling #############################################
+ #
+ # see func_block_disable_msg.py test case for expected behaviour
+
+ def process_tokens(self, tokens):
+ """process tokens from the current module to search for module/block
+ level options
+ """
+ comment = tokenize.COMMENT
+ newline = tokenize.NEWLINE
+ #line_num = 0
+ for (tok_type, _, start, _, line) in tokens:
+ if tok_type not in (comment, newline):
+ continue
+ #if start[0] == line_num:
+ # continue
+ match = OPTION_RGX.search(line)
+ if match is None:
+ continue
+ if match.group(1).strip() == "disable-all":
+ self.add_message('I0013', line=start[0])
+ self._ignore_file = True
+ return
+ try:
+ opt, value = match.group(1).split('=', 1)
+ except ValueError:
+ self.add_message('I0010', args=match.group(1).strip(),
+ line=start[0])
+ continue
+ opt = opt.strip()
+ #line_num = start[0]
+ if opt in self._options_methods and not opt.endswith('-report'):
+ meth = self._options_methods[opt]
+ for msgid in get_csv(value):
+ try:
+ meth(msgid, 'module', start[0])
+ except UnknownMessage:
+ self.add_message('E0012', args=msgid, line=start[0])
+ else:
+ self.add_message('E0011', args=opt, line=start[0])
+
+ def collect_block_lines(self, node, msg_state):
+ """walk ast to collect block level options line numbers"""
+ # recurse on children (depth first)
+ for child in node.getChildNodes():
+ self.collect_block_lines(child, msg_state)
+ first = node.source_line()
+ last = node.last_source_line()
+ for msgid, lines in msg_state.items():
+ for lineno, state in lines.items():
+ if first <= lineno <= last:
+ # set state for all lines for this block
+ first, last = node.block_range(lineno)
+ for line in xrange(first, last+1):
+ # do not override existing entries
+ if not line in self._module_msgs_state.get(msgid, ()):
+ if line in lines: # state change in the same block
+ state = lines[line]
+ try:
+ self._module_msgs_state[msgid][line] = state
+ except KeyError:
+ self._module_msgs_state[msgid] = {line: state}
+ del lines[lineno]
+
+
+ # code checking methods ###################################################
+
+ def check(self, files_or_modules):
+ """main checking entry: check a list of files or modules from their
+ name.
+ """
+ self.reporter.include_ids = self.config.include_ids
+ if not isinstance(files_or_modules, (list, tuple)):
+ files_or_modules = (files_or_modules,)
+ filemods = self.expand_files(files_or_modules)
+ checkers = sort_checkers(self._checkers.values())
+ rev_checkers = checkers[:]
+ rev_checkers.reverse()
+ # notify global begin
+ for checker in checkers:
+ checker.open()
+ # prebuild ast for all modules to check
+ for descr in filemods[:]:
+ modname, filepath = descr['name'], descr['path']
+ self.set_current_module(modname, filepath)
+ # get the module representation
+ try:
+ astng = self.get_astng(filepath, modname)
+ except SyntaxError, ex:
+ self.add_message('E0001', line=ex.lineno, args=ex.msg)
+ astng = None
+ if astng is None:
+ filemods.remove(descr)
+ continue
+ descr['astng'] = astng
+ # check modules or packages
+ for descr in filemods:
+ modname, filepath = descr['name'], descr['path']
+ astng = descr['astng']
+ self.base_name = descr['basename']
+ self.base_file = descr['basepath']
+ if self.config.files_output:
+ reportfile = 'pylint_%s.%s' % (modname, self.reporter.extension)
+ self.reporter.set_output(open(reportfile, 'w'))
+ self.set_current_module(modname, filepath)
+ self._ignore_file = False
+ # fix the current file (if the source file was not available or
+ # if its actually a c extension
+ self.current_file = astng.file
+ self.check_astng_module(astng, checkers)
+ # notify global end
+ self.set_current_module('')
+ for checker in rev_checkers:
+ checker.close()
+
+ def expand_files(self, files_or_modules):
+ """take a list of files/modules/packages and return the list of tuple
+ (file, module name) which have to be actually checked
+ """
+ result = []
+ for something in files_or_modules:
+ if exists(something):
+ # this is a file or a directory
+ try:
+ modname = '.'.join(modpath_from_file(something))
+ except ImportError:
+ modname = splitext(basename(something))[0]
+ if isdir(something):
+ filepath = join(something, '__init__.py')
+ else:
+ filepath = something
+ else:
+ # suppose it's a module or package
+ modname = something
+ try:
+ filepath = file_from_modpath(modname.split('.'))
+ if filepath is None:
+ self.set_current_module(modname)
+ self.add_message('F0003', args=modname)
+ continue
+ except ImportError, ex:
+ #if __debug__:
+ # import traceback
+ # traceback.print_exc()
+ self.set_current_module(modname)
+ msg = str(ex).replace(os.getcwd() + os.sep, '')
+ self.add_message('F0001', args=msg)
+ continue
+ filepath = normpath(filepath)
+ result.append( {'path': filepath, 'name': modname,
+ 'basepath': filepath, 'basename': modname} )
+ if not (modname.endswith('.__init__') or modname == '__init__') \
+ and '__init__.py' in filepath:
+ for subfilepath in get_module_files(dirname(filepath),
+ self.config.black_list):
+ if filepath == subfilepath:
+ continue
+ submodname = '.'.join(modpath_from_file(subfilepath))
+ result.append( {'path': subfilepath, 'name': submodname,
+ 'basepath': filepath, 'basename': modname} )
+ return result
+
+ def set_current_module(self, modname, filepath=None):
+ """set the name of the currently analyzed module and
+ init statistics for it
+ """
+ if not modname and filepath is None:
+ return
+ self.current_name = modname
+ self.current_file = filepath or modname
+ self.stats['by_module'][modname] = {}
+ self.stats['by_module'][modname]['statement'] = 0
+ for msg_cat in MSG_TYPES.values():
+ self.stats['by_module'][modname][msg_cat] = 0
+ # XXX hack, to be correct we need to keep module_msgs_state
+ # for every analyzed module (the problem stands with localized
+ # messages which are only detected in the .close step)
+ if modname:
+ self._module_msgs_state = {}
+ self._module_msg_cats_state = {}
+
+ def get_astng(self, filepath, modname):
+ """return a astng representation for a module"""
+ try:
+ return self.manager.astng_from_file(filepath, modname)
+ except SyntaxError, ex:
+ self.add_message('E0001', line=ex.lineno, args=ex.msg)
+ except KeyboardInterrupt:
+ raise
+ except Exception, ex:
+ #if __debug__:
+ # import traceback
+ # traceback.print_exc()
+ self.add_message('F0002', args=(ex.__class__, ex))
+
+
+ def check_astng_module(self, astng, checkers):
+ """check a module from its astng representation, real work"""
+ # call raw checkers if possible
+ if not astng.pure_python:
+ self.add_message('I0001', args=astng.name)
+ else:
+ #assert astng.file.endswith('.py')
+ stream = norm_open(astng.file)
+ # invoke IRawChecker interface on self to fetch module/block
+ # level options
+ self.process_module(stream)
+ if self._ignore_file:
+ return False
+ # walk ast to collect line numbers
+ orig_state = self._module_msgs_state.copy()
+ self._module_msgs_state = {}
+ self.collect_block_lines(astng, orig_state)
+ for checker in checkers:
+ if implements(checker, IRawChecker) and checker is not self:
+ stream.seek(0)
+ checker.process_module(stream)
+ # generate events to astng checkers
+ self.astng_events(astng, [checker for checker in checkers
+ if implements(checker, IASTNGChecker)])
+ return True
+
+ def astng_events(self, astng, checkers, _reversed_checkers=None):
+ """generate event to astng checkers according to the current astng
+ node and recurse on its children
+ """
+ if _reversed_checkers is None:
+ _reversed_checkers = checkers[:]
+ _reversed_checkers.reverse()
+ if astng.is_statement():
+ self.stats['statement'] += 1
+ # generate events for this node on each checkers
+ for checker in checkers:
+ checker.visit(astng)
+ # recurse on children
+ for child in astng.getChildNodes():
+ self.astng_events(child, checkers, _reversed_checkers)
+ for checker in _reversed_checkers:
+ checker.leave(astng)
+
+
+ # IASTNGChecker interface #################################################
+
+ def open(self):
+ """initialize counters"""
+ self.stats = { 'by_module' : {},
+ 'by_msg' : {},
+ 'statement' : 0
+ }
+ for msg_cat in MSG_TYPES.values():
+ self.stats[msg_cat] = 0
+
+ def close(self):
+ """close the whole package /module, it's time to make reports !
+
+ if persistent run, pickle results for later comparison
+ """
+ if self.base_name is not None:
+ # load old results if any
+ old_stats = config.load_results(self.base_name)
+ if self.config.reports:
+ self.make_reports(self.stats, old_stats)
+ # save results if persistent run
+ if self.config.persistent:
+ config.save_results(self.stats, self.base_name)
+
+ # specific reports ########################################################
+
+ def report_evaluation(self, sect, stats, old_stats):
+ """make the global evaluation report"""
+ # check with at least check 1 statements (usually 0 when there is a
+ # syntax error preventing pylint from further processing)
+ if stats['statement'] == 0:
+ raise EmptyReport()
+ # get a global note for the code
+ evaluation = self.config.evaluation
+ try:
+ note = eval(evaluation, {}, self.stats)
+ except Exception, ex:
+ msg = 'An exception occured while rating: %s' % ex
+ else:
+ stats['global_note'] = note
+ msg = 'Your code has been rated at %.2f/10' % note
+ if old_stats.has_key('global_note'):
+ msg += ' (previous run: %.2f/10)' % old_stats['global_note']
+ if self.config.comment:
+ msg = '%s\n%s' % (msg, config.get_note_message(note))
+ sect.append(Text(msg))
+
+# some reporting functions ####################################################
+
+def report_total_messages_stats(sect, stats, old_stats):
+ """make total errors / warnings report"""
+ lines = ['type', 'number', 'previous', 'difference']
+ lines += table_lines_from_stats(stats, old_stats,
+ ('convention', 'refactor',
+ 'warning', 'error'))
+ sect.append(Table(children=lines, cols=4, rheaders=1))
+
+def report_messages_stats(sect, stats, _):
+ """make messages type report"""
+ if not stats['by_msg']:
+ # don't print this report when we didn't detected any errors
+ raise EmptyReport()
+ in_order = [(value, msg_id)
+ for msg_id, value in stats['by_msg'].items()
+ if not msg_id.startswith('I')]
+ in_order.sort()
+ in_order.reverse()
+ lines = ('message id', 'occurences')
+ for value, msg_id in in_order:
+ lines += (msg_id, str(value))
+ sect.append(Table(children=lines, cols=2, rheaders=1))
+
+def report_messages_by_module_stats(sect, stats, _):
+ """make errors / warnings by modules report"""
+ if len(stats['by_module']) == 1:
+ # don't print this report when we are analysing a single module
+ raise EmptyReport()
+ by_mod = {}
+ for m_type in ('fatal', 'error', 'warning', 'refactor', 'convention'):
+ total = stats[m_type]
+ for module in stats['by_module'].keys():
+ mod_total = stats['by_module'][module][m_type]
+ if total == 0:
+ percent = 0
+ else:
+ percent = float((mod_total)*100) / total
+ by_mod.setdefault(module, {})[m_type] = percent
+ sorted_result = []
+ for module, mod_info in by_mod.items():
+ sorted_result.append((mod_info['error'],
+ mod_info['warning'],
+ mod_info['refactor'],
+ mod_info['convention'],
+ module))
+ sorted_result.sort()
+ sorted_result.reverse()
+ lines = ['module', 'error', 'warning', 'refactor', 'convention']
+ for line in sorted_result:
+ if line[0] == 0 and line[1] == 0:
+ break
+ lines.append(line[-1])
+ for val in line[:-1]:
+ lines.append('%.2f' % val)
+ if len(lines) == 5:
+ raise EmptyReport()
+ sect.append(Table(children=lines, cols=5, rheaders=1))
+
+
+
+# utilities ###################################################################
+
+# this may help to import modules using gettext
+
+try:
+ __builtins__._ = str
+except AttributeError:
+ __builtins__['_'] = str
+
+
+def preprocess_options(args, search_for):
+ """look for some options (keys of <search_for>) which have to be processed
+ before others
+
+ values of <search_for> are callback functions to call when the option is
+ found
+ """
+ i = 0
+ while i < len(args):
+ arg = args[i]
+ if arg.startswith('--'):
+ try:
+ option, val = arg[2:].split('=', 1)
+ except ValueError:
+ option, val = arg[2:], None
+ try:
+ cb, takearg = search_for[option]
+ del args[i]
+ if takearg and val is None:
+ val = args[i]
+ del args[i]
+ cb(option, val)
+ except KeyError:
+ i += 1
+ else:
+ i += 1
+
+class Run:
+ """helper class to use as main for pylint :
+
+ run(*sys.argv[1:])
+ """
+ LinterClass = PyLinter
+ option_groups = (
+ ('Commands', 'Options which are actually commands. Options in this \
+group are mutually exclusive.'),
+ )
+
+ def __init__(self, args, reporter=None):
+ self._rcfile = None
+ self._plugins = []
+ preprocess_options(args, {
+ # option: (callback, takearg)
+ 'rpython-mode': (self.cb_rpython_mode, False),
+ 'rcfile': (self.cb_set_rcfile, True),
+ 'load-plugins': (self.cb_add_plugins, True),
+ })
+ self.linter = linter = self.LinterClass((
+ ('rcfile',
+ {'action' : 'callback', 'callback' : lambda *args: 1,
+ 'type': 'string', 'metavar': '<file>',
+ 'help' : 'Specify a configuration file.'}),
+
+ ('init-hook',
+ {'action' : 'callback', 'type' : 'string', 'metavar': '<code>',
+ 'callback' : cb_init_hook,
+ 'help' : 'Python code to execute, usually for sys.path \
+manipulation such as pygtk.require().'}),
+
+ ('rpython-mode',
+ {'action' : 'callback', 'callback' : lambda *args: 1,
+ 'help' : 'enable the rpython checker which is disabled by default'
+ }),
+ #'help' : 'Run into Restricted Python analysis mode.'}),
+
+ ('help-msg',
+ {'action' : 'callback', 'type' : 'string', 'metavar': '<msg-id>',
+ 'callback' : self.cb_help_message,
+ 'group': 'Commands',
+ 'help' : '''Display a help message for the given message id and \
+exit. The value may be a comma separated list of message ids.'''}),
+
+ ('list-msgs',
+ {'action' : 'callback', 'metavar': '<msg-id>',
+ 'callback' : self.cb_list_messages,
+ 'group': 'Commands',
+ 'help' : "Generate pylint's full documentation."}),
+
+ ('generate-rcfile',
+ {'action' : 'callback', 'callback' : self.cb_generate_config,
+ 'group': 'Commands',
+ 'help' : '''Generate a sample configuration file according to \
+the current configuration. You can put other options before this one to get \
+them in the generated configuration.'''}),
+
+ ('generate-man',
+ {'action' : 'callback', 'callback' : self.cb_generate_manpage,
+ 'group': 'Commands',
+ 'help' : "Generate pylint's man page."}),
+
+ ('errors-only',
+ {'action' : 'callback', 'callback' : self.cb_debug_mode,
+ 'short': 'e',
+ 'help' : '''In debug mode, checkers without error messages are \
+disabled and for others, only the ERROR messages are displayed, and no reports \
+are done by default'''}),
+
+ ('profile',
+ {'type' : 'yn', 'metavar' : '<y_or_n>',
+ 'default': False,
+ 'help' : 'Profiled execution.'}),
+
+ ), option_groups=self.option_groups,
+ reporter=reporter, pylintrc=self._rcfile)
+ # register standard checkers
+ from pylint import checkers
+ checkers.initialize(linter)
+ # load command line plugins
+ linter.load_plugin_modules(self._plugins)
+ # add some help section
+ linter.add_help_section('Environment variables', config.ENV_HELP)
+ linter.add_help_section('Output', '''
+Using the default text output, the message format is :
+ MESSAGE_TYPE: LINE_NUM:[OBJECT:] MESSAGE
+There are 5 kind of message types :
+ * (C) convention, for programming standard violation
+ * (R) refactor, for bad code smell
+ * (W) warning, for python specific problems
+ * (E) error, for probable bugs in the code
+ * (F) fatal, if an error occured which prevented pylint from doing further \
+processing.
+ ''')
+ # read configuration
+ #linter.load_provider_defaults()
+ linter.read_config_file()
+ # is there some additional plugins in the file configuration, in
+ config_parser = linter._config_parser
+ if config_parser.has_option('MASTER', 'load-plugins'):
+ plugins = get_csv(config_parser.get('MASTER', 'load-plugins'))
+ linter.load_plugin_modules(plugins)
+ # now we can load file config and command line, plugins (which can
+ # provide options) have been registered
+ linter.load_config_file()
+ if reporter:
+ # if a custom reporter is provided as argument, it may be overriden
+ # by file parameters, so re-set it here, but before command line
+ # parsing so it's still overrideable by command line option
+ linter.set_reporter(reporter)
+ args = linter.load_command_line_configuration(args)
+ if not args:
+ print linter.help()
+ sys.exit(1)
+ # insert current working directory to the python path to have a correct
+ # behaviour
+ sys.path.insert(0, os.getcwd())
+ if self.linter.config.profile:
+ print >> sys.stderr, '** profiled run'
+ from hotshot import Profile, stats
+ prof = Profile('stones.prof')
+ prof.runcall(linter.check, args)
+ prof.close()
+ data = stats.load('stones.prof')
+ data.strip_dirs()
+ data.sort_stats('time', 'calls')
+ data.print_stats(30)
+ else:
+ linter.check(args)
+ sys.path.pop(0)
+
+ def cb_rpython_mode(self, name, value):
+ from pylint.checkers.rpython import RPythonChecker
+ RPythonChecker.enabled = True
+ #from pylint.rlint import RPyLinter
+ #self.LinterClass = RPyLinter
+
+ def cb_set_rcfile(self, name, value):
+ """callback for option preprocessing (ie before optik parsing)"""
+ self._rcfile = value
+
+ def cb_add_plugins(self, name, value):
+ """callback for option preprocessing (ie before optik parsing)"""
+ self._plugins.extend(get_csv(value))
+
+ def cb_debug_mode(self, *args, **kwargs):
+ """debug mode:
+ * checkers without error messages are disabled
+ * for others, only the ERROR messages are displayed
+ * disable reports
+ * do not save execution information
+ """
+ self.linter.disable_noerror_checkers()
+ self.linter.set_option('disable-msg-cat', ('W', 'C', 'R', 'F', 'I'))
+ self.linter.set_option('reports', False)
+ self.linter.set_option('persistent', False)
+
+
+ def cb_generate_config(self, *args, **kwargs):
+ """optik callback for sample config file generation"""
+ self.linter.generate_config(skipsections=('COMMANDS',))
+ sys.exit(0)
+
+ def cb_generate_manpage(self, *args, **kwargs):
+ """optik callback for sample config file generation"""
+ from pylint import __pkginfo__
+ self.linter.generate_manpage(__pkginfo__)
+ sys.exit(0)
+
+ def cb_help_message(self, option, opt_name, value, parser):
+ """optik callback for printing some help about a particular message"""
+ self.linter.help_message(get_csv(value))
+ sys.exit(0)
+
+ def cb_list_messages(self, option, opt_name, value, parser):
+ """optik callback for printing available messages"""
+ self.linter.list_messages()
+ sys.exit(0)
+
+def cb_init_hook(option, opt_name, value, parser):
+ """exec arbitrary code to set sys.path for instance"""
+ exec value
+
+
+if __name__ == '__main__':
+ Run(sys.argv[1:])
diff --git a/man/pylint.1 b/man/pylint.1
new file mode 100644
index 0000000..1e58bc5
--- /dev/null
+++ b/man/pylint.1
@@ -0,0 +1,252 @@
+.TH pylint 1 "2008-2-7" pylint
+.SH NAME
+.B pylint
+\- python code static checker
+
+.SH SYNOPSIS
+.B pylint
+[
+.I OPTIONS
+] [
+.I <arguments>
+]
+
+.SH DESCRIPTION
+.B pylint
+is a Python source code analyzer which looks for programming
+errors, helps enforcing a coding standard and sniffs for some code
+smells (as defined in Martin Fowler's Refactoring book)
+
+Pylint can be seen as another PyChecker since nearly all tests you
+can do with PyChecker can also be done with Pylint. However, Pylint
+offers some more features, like checking length of lines of code,
+checking if variable names are well-formed according to your coding
+standard, or checking if declared interfaces are truly implemented,
+and much more.
+
+Additionally, it is possible to write plugins to add your own checks.
+
+.SH OPTIONS
+.IP "--version"
+show program's version number and exit
+.IP "--help, -h"
+show this help message and exit
+
+.SH MASTER
+.IP "--rcfile=<file>"
+Specify a configuration file.
+.IP "--init-hook=<code>"
+Python code to execute, usually for sys.path manipulation such as pygtk.require().
+.IP "--rpython-mode"
+enable the rpython checker which is disabled by default
+.IP "--errors-only, -e"
+In debug mode, checkers without error messages are disabled and for others, only the ERROR messages are displayed, and no reports are done by default
+.IP "--profile=<y_or_n>"
+Profiled execution. [current: %default]
+.IP "--ignore=<file>"
+Add <file or directory> to the black list. It should be a base name, not a path. You may set this option multiple times. [current: %default]
+.IP "--persistent=<y_or_n>"
+Pickle collected data for later comparisons. [current: %default]
+.IP "--cache-size=<size>"
+Set the cache size for astng objects. [current: %default]
+.IP "--load-plugins=<modules>"
+List of plugins (as comma separated values of python modules names) to load, usually to register additional checkers. [current: %default]
+
+.SH COMMANDS
+.IP "--help-msg=<msg-id>"
+Display a help message for the given message id and exit. The value may be a comma separated list of message ids.
+.IP "--list-msgs"
+Generate pylint's full documentation.
+.IP "--generate-rcfile"
+Generate a sample configuration file according to the current configuration. You can put other options before this one to get them in the generated configuration.
+.IP "--generate-man"
+Generate pylint's man page.
+
+.SH MESSAGES CONTROL
+.IP "--enable-checker=<checker ids>"
+Enable only checker(s) with the given id(s). This option conflicts with the disable-checker option
+.IP "--disable-checker=<checker ids>"
+Enable all checker(s) except those with the given id(s). This option conflicts with the enable-checker option
+.IP "--enable-msg-cat=<msg cats>"
+Enable all messages in the listed categories.
+.IP "--disable-msg-cat=<msg cats>"
+Disable all messages in the listed categories.
+.IP "--enable-msg=<msg ids>"
+Enable the message(s) with the given id(s).
+.IP "--disable-msg=<msg ids>"
+Disable the message(s) with the given id(s).
+
+.SH REPORTS
+.IP "--output-format=<format>, -f <format>"
+Set the output format. Available formats are text, parseable, colorized, msvs (visual studio) and html [current: %default]
+.IP "--include-ids=<y_or_n>, -i <y_or_n>"
+Include message's id in output [current: %default]
+.IP "--files-output=<y_or_n>"
+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]". [current: %default]
+.IP "--reports=<y_or_n>, -r <y_or_n>"
+Tells wether to display a full report or only the messages [current: %default]
+.IP "--evaluation=<python_expression>"
+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 respectivly contain the number of errors / warnings messages and the total number of statements analyzed. This is used by the global evaluation report (R0004). [current: %default]
+.IP "--comment=<y_or_n>"
+Add a comment according to your evaluation note. This is used by the global evaluation report (R0004). [current: %default]
+.IP "--enable-report=<rpt ids>"
+Enable the report(s) with the given id(s).
+.IP "--disable-report=<rpt ids>"
+Disable the report(s) with the given id(s).
+
+.SH BASIC
+.IP "--required-attributes=<attributes>"
+Required attributes for module, separated by a comma [current: %default]
+.IP "--no-docstring-rgx=<regexp>"
+Regular expression which should only match functions or classes name which do not require a docstring [current: %default]
+.IP "--module-rgx=<regexp>"
+Regular expression which should only match correct module names [current: %default]
+.IP "--const-rgx=<regexp>"
+Regular expression which should only match correct module level names [current: %default]
+.IP "--class-rgx=<regexp>"
+Regular expression which should only match correct class names [current: %default]
+.IP "--function-rgx=<regexp>"
+Regular expression which should only match correct function names [current: %default]
+.IP "--method-rgx=<regexp>"
+Regular expression which should only match correct method names [current: %default]
+.IP "--attr-rgx=<regexp>"
+Regular expression which should only match correct instance attribute names [current: %default]
+.IP "--argument-rgx=<regexp>"
+Regular expression which should only match correct argument names [current: %default]
+.IP "--variable-rgx=<regexp>"
+Regular expression which should only match correct variable names [current: %default]
+.IP "--inlinevar-rgx=<regexp>"
+Regular expression which should only match correct list comprehension / generator expression variable names [current: %default]
+.IP "--good-names=<names>"
+Good variable names which should always be accepted, separated by a comma [current: %default]
+.IP "--bad-names=<names>"
+Bad variable names which should always be refused, separated by a comma [current: %default]
+.IP "--bad-functions=<builtin function names>"
+List of builtins function names that should not be used, separated by a comma [current: %default]
+
+.SH CLASSES
+.IP "--ignore-iface-methods=<method names>"
+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. [current: %default]
+.IP "--defining-attr-methods=<method names>"
+List of method names used to declare (i.e. assign) instance attributes. [current: %default]
+
+.SH DESIGN
+.IP "--max-args=<int>"
+Maximum number of arguments for function / method [current: %default]
+.IP "--max-locals=<int>"
+Maximum number of locals for function / method body [current: %default]
+.IP "--max-returns=<int>"
+Maximum number of return / yield for function / method body [current: %default]
+.IP "--max-branchs=<int>"
+Maximum number of branch for function / method body [current: %default]
+.IP "--max-statements=<int>"
+Maximum number of statements in function / method body [current: %default]
+.IP "--max-parents=<num>"
+Maximum number of parents for a class (see R0901). [current: %default]
+.IP "--max-attributes=<num>"
+Maximum number of attributes for a class (see R0902). [current: %default]
+.IP "--min-public-methods=<num>"
+Minimum number of public methods for a class (see R0903). [current: %default]
+.IP "--max-public-methods=<num>"
+Maximum number of public methods for a class (see R0904). [current: %default]
+
+.SH FORMAT
+.IP "--max-line-length=<int>"
+Maximum number of characters on a single line. [current: %default]
+.IP "--max-module-lines=<int>"
+Maximum number of lines in a module [current: %default]
+.IP "--indent-string=<string>"
+String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 tab). [current: %default]
+
+.SH IMPORTS
+.IP "--deprecated-modules=<modules>"
+Deprecated modules which should not be used, separated by a comma [current: %default]
+.IP "--import-graph=<file.dot>"
+Create a graph of every (i.e. internal and external) dependencies in the given file (report R0402 must not be disabled) [current: %default]
+.IP "--ext-import-graph=<file.dot>"
+Create a graph of external dependencies in the given file (report R0402 must not be disabled) [current: %default]
+.IP "--int-import-graph=<file.dot>"
+Create a graph of internal dependencies in the given file (report R0402 must not be disabled) [current: %default]
+
+.SH MISCELLANEOUS
+.IP "--notes=<comma separated values>"
+List of note tags to take in consideration, separated by a comma. [current: %default]
+
+.SH SIMILARITIES
+.IP "--min-similarity-lines=<int>"
+Minimum lines number of a similarity. [current: %default]
+.IP "--ignore-comments=<y or n>"
+Ignore comments when computing similarities. [current: %default]
+.IP "--ignore-docstrings=<y or n>"
+Ignore docstrings when computing similarities. [current: %default]
+
+.SH TYPECHECK
+.IP "--ignore-mixin-members=<y_or_n>"
+Tells wether missing members accessed in mixin class should be ignored. A mixin class is detected if its name ends with "mixin" (case insensitive). [current: %default]
+.IP "--ignored-classes=<members names>"
+List of classes names for which member attributes should not be checked (useful for classes with attributes dynamicaly set). [current: %default]
+.IP "--zope=<y_or_n>"
+When zope mode is activated, consider the acquired-members option to ignore access to some undefined attributes. [current: %default]
+.IP "--acquired-members=<members names>"
+List of members which are usually get through zope's acquisition mecanism and so shouldn't trigger E0201 when accessed (need zope=yes to be considered). [current: %default]
+
+.SH VARIABLES
+.IP "--init-import=<y_or_n>"
+Tells wether we should check for unused import in __init__ files. [current: %default]
+.IP "--dummy-variables-rgx=<regexp>"
+A regular expression matching names used for dummy variables (i.e. not used). [current: %default]
+.IP "--additional-builtins=<comma separated list>"
+List of additional names supposed to be defined in builtins. Remember that you should avoid to define new builtins when possible. [current: %default]
+
+.SH ENVIRONMENT VARIABLES
+
+The following environment variables are used :
+ * PYLINTHOME
+ path to the directory where data of persistent run will be stored. If not
+found, it defaults to ~/.pylint.d/ or .pylint.d (in the current working
+directory) . The current PYLINTHOME is /home/syt/.pylint.d.
+ * PYLINTRC
+ path to the configuration file. If not found, it will use the first
+existant file in ~/.pylintrc, /etc/pylintrc. The current PYLINTRC is
+None.
+
+.SH OUTPUT
+
+Using the default text output, the message format is :
+ MESSAGE_TYPE: LINE_NUM:[OBJECT:] MESSAGE
+There are 5 kind of message types :
+ * (C) convention, for programming standard violation
+ * (R) refactor, for bad code smell
+ * (W) warning, for python specific problems
+ * (E) error, for probable bugs in the code
+ * (F) fatal, if an error occured which prevented pylint from doing further processing.
+
+.SH SEE ALSO
+/usr/share/doc/pythonX.Y-pylint/
+
+.SH COPYRIGHT
+Copyright (c) 2003-2008 Sylvain Thenault (thenault@gmail.com).
+Copyright (c) 2003-2008 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., 59 Temple Place, Suite 330, Boston,
+MA 02111-1307 USA.
+.SH BUGS
+Please report bugs on the project's mailing list:
+mailto://python-projects@logilab.org
+
+.SH AUTHOR
+Sylvain Thenault <sylvain.thenault@logilab.fr>
+
diff --git a/reporters/__init__.py b/reporters/__init__.py
new file mode 100644
index 0000000..b0e1bd5
--- /dev/null
+++ b/reporters/__init__.py
@@ -0,0 +1,67 @@
+# 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.,
+# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+"""utilities methods and classes for reporters
+
+ Copyright (c) 2000-2003 LOGILAB S.A. (Paris, FRANCE).
+ http://www.logilab.fr/ -- mailto:contact@logilab.fr
+"""
+
+import sys
+
+CMPS = ['=', '-', '+']
+
+def diff_string(old, new):
+ """given a old and new int value, return a string representing the
+ difference
+ """
+ diff = abs(old - new)
+ diff_str = "%s%s" % (CMPS[cmp(old, new)], diff and ('%.2f' % diff) or '')
+ return diff_str
+
+
+class EmptyReport(Exception):
+ """raised when a report is empty and so should not be displayed"""
+
+class BaseReporter:
+ """base class for reporters"""
+
+ extension = ''
+
+ def __init__(self, output=None):
+ self.linter = None
+ self.include_ids = None
+ self.section = 0
+ self.out = None
+ self.set_output(output)
+
+ def set_output(self, output=None):
+ """set output stream"""
+ if output is None:
+ output = sys.stdout
+ self.out = output
+
+ def writeln(self, string=''):
+ """write a line in the output buffer"""
+ print >> self.out, string
+
+ def display_results(self, layout):
+ """display results encapsulated in the layout tree"""
+ self.section = 0
+ if self.include_ids and hasattr(layout, 'report_id'):
+ layout.children[0].children[0].data += ' (%s)' % layout.report_id
+ self._display(layout)
+
+ def _display(self, layout):
+ """display the layout"""
+ raise NotImplementedError()
+
diff --git a/reporters/html.py b/reporters/html.py
new file mode 100644
index 0000000..d25cc2e
--- /dev/null
+++ b/reporters/html.py
@@ -0,0 +1,65 @@
+# Copyright (c) 2003-2006 Sylvain Thenault (thenault@gmail.com).
+# Copyright (c) 2003-2006 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.,
+# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+"""HTML reporter
+"""
+
+__revision__ = "$Id: html.py,v 1.14 2006-03-08 15:53:41 syt Exp $"
+
+import sys
+from cgi import escape
+
+from logilab.common.ureports import HTMLWriter, Section, Table
+
+from pylint.interfaces import IReporter
+from pylint.reporters import BaseReporter
+
+
+class HTMLReporter(BaseReporter):
+ """report messages and layouts in HTML
+ """
+
+ __implements__ = IReporter
+ extension = 'html'
+
+ def __init__(self, output=sys.stdout):
+ BaseReporter.__init__(self, output)
+ self.msgs = []
+
+ def add_message(self, msg_id, location, msg):
+ """manage message of different type and in the context of path"""
+ module, obj, line = location[1:]
+ if self.include_ids:
+ sigle = msg_id
+ else:
+ sigle = msg_id[0]
+ self.msgs += [sigle, module, obj, str(line), escape(msg)]
+
+ def _display(self, layout):
+ """launch layouts display
+
+ overridden from BaseReporter to add insert the messages section
+ (in add_message, message is not displayed, just collected so it
+ can be displayed in an html table)
+ """
+ if self.msgs:
+ # add stored messages to the layout
+ msgs = ['type', 'module', 'object', 'line', 'message']
+ msgs += self.msgs
+ sect = Section('Messages')
+ layout.append(sect)
+ sect.append(Table(cols=5, children=msgs, rheaders=1))
+ self.msgs = []
+ HTMLWriter().format(layout, self.out)
+
diff --git a/reporters/text.py b/reporters/text.py
new file mode 100644
index 0000000..40c28ec
--- /dev/null
+++ b/reporters/text.py
@@ -0,0 +1,156 @@
+# Copyright (c) 2003-2007 Sylvain Thenault (thenault@gmail.com).
+# Copyright (c) 2003-2007 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.,
+# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+"""Plain text reporters:
+
+:text: the default one grouping messages by module
+:parseable:
+ standard parseable output with full module path on each message (for
+ editor integration)
+:colorized: an ANSI colorized text reporter
+
+"""
+
+import os
+import sys
+
+from logilab.common.ureports import TextWriter
+from logilab.common.textutils import colorize_ansi
+
+from pylint.interfaces import IReporter
+from pylint.reporters import BaseReporter
+
+TITLE_UNDERLINES = ['', '=', '-', '.']
+
+
+class TextReporter(BaseReporter):
+ """reports messages and layouts in plain text
+ """
+
+ __implements__ = IReporter
+ extension = 'txt'
+
+ def __init__(self, output=sys.stdout):
+ BaseReporter.__init__(self, output)
+ self._modules = {}
+
+ def add_message(self, msg_id, location, msg):
+ """manage message of different type and in the context of path"""
+ module, obj, line = location[1:]
+ if not self._modules.has_key(module):
+ if module:
+ self.writeln('************* Module %s' % module)
+ self._modules[module] = 1
+ else:
+ self.writeln('************* %s' % module)
+ if obj:
+ obj = ':%s' % obj
+ if self.include_ids:
+ sigle = msg_id
+ else:
+ sigle = msg_id[0]
+ self.writeln('%s:%3s%s: %s' % (sigle, line, obj, msg))
+
+ def _display(self, layout):
+ """launch layouts display"""
+ print >> self.out
+ TextWriter().format(layout, self.out)
+
+
+class ParseableTextReporter(TextReporter):
+ """a reporter very similar to TextReporter, but display messages in a form
+ recognized by most text editors :
+
+ <filename>:<linenum>:<msg>
+ """
+ line_format = '%(path)s:%(line)s: [%(sigle)s%(obj)s] %(msg)s'
+
+ def __init__(self, output=sys.stdout, relative=True):
+ TextReporter.__init__(self, output)
+ if relative:
+ self._prefix = os.getcwd() + os.sep
+ else:
+ self._prefix = ''
+
+ def add_message(self, msg_id, location, msg):
+ """manage message of different type and in the context of path"""
+ path, _, obj, line = location
+ if obj:
+ obj = ', %s' % obj
+ if self.include_ids:
+ sigle = msg_id
+ else:
+ sigle = msg_id[0]
+ if self._prefix:
+ path = path.replace(self._prefix, '')
+ self.writeln(self.line_format % locals())
+
+class VSTextReporter(ParseableTextReporter):
+ """Visual studio text reporter"""
+ line_format = '%(path)s(%(line)s): [%(sigle)s%(obj)s] %(msg)s'
+
+class ColorizedTextReporter(TextReporter):
+ """Simple TextReporter that colorizes text output"""
+
+ COLOR_MAPPING = {
+ "I" : ("green", None),
+ 'C' : (None, "bold"),
+ 'R' : ("magenta", "bold, italic"),
+ 'W' : ("blue", None),
+ 'E' : ("red", "bold"),
+ 'F' : ("red", "bold, underline"),
+ 'S' : ("yellow", "inverse"), # S stands for module Separator
+ }
+
+ def __init__(self, output=sys.stdout, color_mapping = None):
+ TextReporter.__init__(self, output)
+ self.color_mapping = color_mapping or \
+ dict(ColorizedTextReporter.COLOR_MAPPING)
+
+
+ def _get_decoration(self, msg_id):
+ """Returns the tuple color, style associated with msg_id as defined
+ in self.color_mapping
+ """
+ try:
+ return self.color_mapping[msg_id[0]]
+ except KeyError:
+ return None, None
+
+ def add_message(self, msg_id, location, msg):
+ """manage message of different types, and colorize output
+ using ansi escape codes
+ """
+ module, obj, line = location[1:]
+ if not self._modules.has_key(module):
+ color, style = self._get_decoration('S')
+ if module:
+ modsep = colorize_ansi('************* Module %s' % module,
+ color, style)
+ else:
+ modsep = colorize_ansi('************* %s' % module,
+ color, style)
+ self.writeln(modsep)
+ self._modules[module] = 1
+ if obj:
+ obj = ':%s' % obj
+ if self.include_ids:
+ sigle = msg_id
+ else:
+ sigle = msg_id[0]
+ color, style = self._get_decoration(sigle)
+ msg = colorize_ansi(msg, color, style)
+ sigle = colorize_ansi(sigle, color, style)
+ self.writeln('%s:%3s%s: %s' % (sigle, line, obj, msg))
+
diff --git a/setup.cfg b/setup.cfg
new file mode 100644
index 0000000..78cfd76
--- /dev/null
+++ b/setup.cfg
@@ -0,0 +1,3 @@
+[bdist_rpm]
+packager = Sylvain Thenault <sylvain.thenault@logilab.fr>
+provides = pylint
diff --git a/setup.py b/setup.py
new file mode 100644
index 0000000..befbfcb
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,188 @@
+#!/usr/bin/env python
+# pylint: disable-msg=W0142,W0403,W0404,E0611,W0613,W0622,W0622,W0704
+# pylint: disable-msg=R0904,C0103
+#
+# Copyright (c) 2003 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.,
+# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+""" Generic Setup script, takes package info from __pkginfo__.py file """
+
+from __future__ import nested_scopes
+
+__revision__ = '$Id: setup.py,v 1.26 2006-03-05 15:47:17 syt Exp $'
+
+import os
+import sys
+import shutil
+from distutils.core import setup
+from distutils.command import install_lib
+from os.path import isdir, exists, join, walk
+
+# import required features
+from __pkginfo__ import modname, version, license, short_desc, long_desc, \
+ web, author, author_email, classifiers
+# import optional features
+try:
+ from __pkginfo__ import distname
+except ImportError:
+ distname = modname
+try:
+ from __pkginfo__ import scripts
+except ImportError:
+ scripts = []
+try:
+ from __pkginfo__ import data_files
+except ImportError:
+ data_files = None
+try:
+ from __pkginfo__ import subpackage_of
+except ImportError:
+ subpackage_of = None
+try:
+ from __pkginfo__ import include_dirs
+except ImportError:
+ include_dirs = []
+try:
+ from __pkginfo__ import ext_modules
+except ImportError:
+ ext_modules = None
+
+BASE_BLACKLIST = ('CVS', 'debian', 'dist', 'build', '__buildlog', '.svn')
+IGNORED_EXTENSIONS = ('.pyc', '.pyo', '.elc')
+
+
+def ensure_scripts(linux_scripts):
+ """
+ Creates the proper script names required for each platform
+ (taken from 4Suite)
+ """
+ from distutils import util
+ if util.get_platform()[:3] == 'win':
+ scripts_ = [script + '.bat' for script in linux_scripts]
+ else:
+ scripts_ = linux_scripts
+ return scripts_
+
+
+def get_packages(directory, prefix):
+ """return a list of subpackages for the given directory
+ """
+ result = []
+ for package in os.listdir(directory):
+ absfile = join(directory, package)
+ if isdir(absfile):
+ if exists(join(absfile, '__init__.py')) or \
+ package in ('test', 'tests'):
+ if prefix:
+ result.append('%s.%s' % (prefix, package))
+ else:
+ result.append(package)
+ result += get_packages(absfile, result[-1])
+ return result
+
+def export(from_dir, to_dir,
+ blacklist=BASE_BLACKLIST,
+ ignore_ext=IGNORED_EXTENSIONS):
+ """make a mirror of from_dir in to_dir, omitting directories and files
+ listed in the black list
+ """
+ def make_mirror(arg, directory, fnames):
+ """walk handler"""
+ for norecurs in blacklist:
+ try:
+ fnames.remove(norecurs)
+ except ValueError:
+ pass
+ for filename in fnames:
+ # don't include binary files
+ if filename[-4:] in ignore_ext:
+ continue
+ if filename[-1] == '~':
+ continue
+ src = '%s/%s' % (directory, filename)
+ dest = to_dir + src[len(from_dir):]
+ print >> sys.stderr, src, '->', dest
+ if os.path.isdir(src):
+ if not exists(dest):
+ os.mkdir(dest)
+ else:
+ if exists(dest):
+ os.remove(dest)
+ shutil.copy2(src, dest)
+ try:
+ os.mkdir(to_dir)
+ except OSError, ex:
+ # file exists ?
+ import errno
+ if ex.errno != errno.EEXIST:
+ raise
+ walk(from_dir, make_mirror, None)
+
+
+EMPTY_FILE = '"""generated file, don\'t modify or your data will be lost"""\n'
+
+class MyInstallLib(install_lib.install_lib):
+ """extend install_lib command to handle package __init__.py and
+ include_dirs variable if necessary
+ """
+ def run(self):
+ """overridden from install_lib class"""
+ install_lib.install_lib.run(self)
+ # create Products.__init__.py if needed
+ if subpackage_of:
+ product_init = join(self.install_dir, subpackage_of, '__init__.py')
+ if not exists(product_init):
+ self.announce('creating %s' % product_init)
+ stream = open(product_init, 'w')
+ stream.write(EMPTY_FILE)
+ stream.close()
+ # manually install included directories if any
+ if include_dirs:
+ if subpackage_of:
+ base = join(subpackage_of, modname)
+ else:
+ base = modname
+ for directory in include_dirs:
+ dest = join(self.install_dir, base, directory)
+ export(directory, dest)
+
+def install(**kwargs):
+ """setup entry point"""
+ if subpackage_of:
+ package = subpackage_of + '.' + modname
+ kwargs['package_dir'] = {package : '.'}
+ packages = [package] + get_packages(os.getcwd(), package)
+ else:
+ kwargs['package_dir'] = {modname : '.'}
+ packages = [modname] + get_packages(os.getcwd(), modname)
+ kwargs['packages'] = packages
+ return setup(name = distname,
+ version = version,
+ license =license,
+ description = short_desc,
+ long_description = long_desc,
+ author = author,
+ author_email = author_email,
+ url = web,
+ classifiers = classifiers,
+ scripts = ensure_scripts(scripts),
+ data_files=data_files,
+ ext_modules=ext_modules,
+ cmdclass={'install_lib': MyInstallLib},
+ **kwargs
+ )
+
+if __name__ == '__main__' :
+ install()
diff --git a/test/fulltest.sh b/test/fulltest.sh
new file mode 100755
index 0000000..a625339
--- /dev/null
+++ b/test/fulltest.sh
@@ -0,0 +1,17 @@
+#!/bin/sh
+
+if [ $@ ] ; then
+ PYVERSIONS=$@
+else
+ PYVERSIONS="2.2 2.3 2.4"
+fi
+
+for ver in $PYVERSIONS; do
+ echo "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"
+ echo `python$ver -V`
+ echo "^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^"
+ python$ver runtests.py
+ echo "^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^"
+ echo `python$ver -V` -OO
+ python$ver -OO runtests.py
+done \ No newline at end of file
diff --git a/test/func_test.py b/test/func_test.py
new file mode 100644
index 0000000..b6e32d4
--- /dev/null
+++ b/test/func_test.py
@@ -0,0 +1,219 @@
+# Copyright (c) 2003-2008 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.,
+# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+"""functional/non regression tests for pylint"""
+
+import unittest
+import sys
+import re
+import new
+from os import linesep
+from os.path import exists
+
+from logilab.common import testlib
+
+from utils import get_tests_info, fix_path, TestReporter
+
+from logilab.astng import MANAGER
+from pylint.lint import PyLinter
+from pylint import checkers
+
+test_reporter = TestReporter()
+linter = PyLinter()
+linter.set_reporter(test_reporter)
+linter.config.persistent = 0
+checkers.initialize(linter)
+linter.global_set_option('required-attributes', ('__revision__',))
+
+PY23 = sys.version_info >= (2, 3)
+PY24 = sys.version_info >= (2, 4)
+PY25 = sys.version_info >= (2, 5)
+
+
+if linesep != '\n':
+ LINE_RGX = re.compile(linesep)
+ def ulines(string):
+ return LINE_RGX.sub('\n', string)
+else:
+ def ulines(string):
+ return string
+
+INFO_TEST_RGX = re.compile('^func_i\d\d\d\d$')
+
+def exception_str(ex):
+ """function used to replace default __str__ method of exception instances"""
+ return 'in %s\n:: %s' % (ex.file, ', '.join(ex.args))
+
+class LintTestUsingModule(testlib.TestCase):
+ package = 'input'
+ linter = linter
+ def setUp(self):
+ MANAGER.set_cache_size(200) # reset cache
+ def test_functionality(self):
+ tocheck = [self.package+'.'+self.module]
+ if self.depends:
+ tocheck += [self.package+'.%s' % name.replace('.py', '')
+ for name, file in self.depends]
+ self._test(tocheck)
+
+ def _test(self, tocheck):
+ if INFO_TEST_RGX.match(self.module):
+ self.linter.enable_message_category('I')
+ else:
+ self.linter.disable_message_category('I')
+ try:
+ self.linter.check(tocheck)
+ except Exception, ex:
+ # need finalization to restore a correct state
+ self.linter.reporter.finalize()
+ ex.file = tocheck
+ ex.__str__ = new.instancemethod(exception_str, ex, None)
+ raise
+ if self.module.startswith('func_noerror_'):
+ expected = ''
+ else:
+ output = open(self.output)
+ expected = output.read().strip()
+ output.close()
+ got = self.linter.reporter.finalize().strip()
+ try:
+ self.assertLinesEquals(got, expected)
+ except Exception, ex:
+ # doesn't work with py 2.5
+ #ex.file = tocheck
+ #ex.__str__ = new.instancemethod(exception_str, ex, None)
+ raise AssertionError('%s: %s' % (self.module, ex)), None, sys.exc_info()[-1]
+
+class LintTestUsingFile(LintTestUsingModule):
+
+ def test_functionality(self):
+ tocheck = [self.package+'/' + self.module + '.py']
+ if self.depends:
+ tocheck += [self.package+'/%s' % name for name, file in self.depends]
+ self._test(tocheck)
+
+
+class TestTests(testlib.TestCase):
+ """check that all testable messages have been checked"""
+ def test(self):
+ # skip rpython checker and fatal messages
+ todo = [msgid for msgid in linter._messages.keys() if msgid[1:3] != '12' and msgid[0] != 'F']
+ for msgid in test_reporter.message_ids.keys():
+ try:
+ todo.remove(msgid)
+ except ValueError:
+ continue
+ todo.sort()
+ if PY25:
+ self.assertEqual(todo, ['E0503', 'E1010', 'I0001'])
+ elif PY23:
+ self.assertEqual(todo, ['E0503', 'I0001'])
+ else: # python < 2.3
+ self.assertEqual(todo, ['I0001'])
+
+#bycat = {}
+#for msgid in linter._messages.keys():
+# bycat[msgid[0]] = bycat.setdefault(msgid[0], 0) + 1
+#for cat, val in bycat.items():
+# print '%s: %s' % (cat, val)
+#print 'total', sum(bycat.values())
+#
+# on 2007/02/17:
+#
+# W: 48
+# E: 42
+# R: 15
+# C: 13
+# F: 7
+# I: 5
+# total 130
+
+def make_tests(filter_rgx):
+ """generate tests classes from test info
+
+ return the list of generated test classes
+ """
+ if filter_rgx:
+ is_to_run = re.compile(filter_rgx).match
+ else:
+ is_to_run = lambda x: 1
+ tests = []
+ for module_file, messages_file in get_tests_info('func_', '.py') + [('nonexistant', 'messages/nonexistant.txt')]:
+ # skip those tests with python >= 2.3 since py2.3 detects them by itself
+ if PY23 and module_file == "func_unknown_encoding.py": #"func_nonascii_noencoding.py"):
+ continue
+ if not PY24:
+ if module_file == "func_noerror_staticmethod_as_decorator.py" or \
+ module_file.endswith('py24.py'):
+ continue
+ if not is_to_run(module_file):
+ continue
+ base = module_file.replace('func_', '').replace('.py', '')
+ dependancies = get_tests_info(base, '.py')
+
+ class LintTestUsingModuleTC(LintTestUsingModule):
+ module = module_file.replace('.py', '')
+ output = messages_file
+ depends = dependancies or None
+ tests.append(LintTestUsingModuleTC)
+
+ if MODULES_ONLY:
+ continue
+
+ class LintTestUsingFileTC(LintTestUsingFile):
+ module = module_file.replace('.py', '')
+ output = exists(messages_file + '2') and (messages_file + '2') or messages_file
+ depends = dependancies or None
+ tests.append(LintTestUsingFileTC)
+
+## # special test for f0003
+## module_file, messages_file in get_tests_info('func_f0003', '.pyc')
+## class LintTestSubclass(LintTest):
+## module = module_file.replace('.pyc', '')
+## output = messages_file
+## depends = dependancies or None
+## tests.append(LintTestSubclass)
+
+ class LintBuiltinModuleTest(LintTestUsingModule):
+ output = 'messages/builtin_module.txt'
+ module = 'sys'
+ def test_functionality(self):
+ self._test(['sys'])
+ tests.append(LintBuiltinModuleTest)
+
+ if not filter_rgx:
+ # test all features are tested :)
+ tests.append(TestTests)
+
+ return tests
+
+FILTER_RGX = None
+MODULES_ONLY = False
+
+def suite():
+ return unittest.TestSuite([unittest.makeSuite(test)
+ for test in make_tests(FILTER_RGX)])
+
+if __name__=='__main__':
+ if '-m' in sys.argv:
+ MODULES_ONLY = True
+ sys.argv.remove('-m')
+
+ if len(sys.argv) > 1:
+ FILTER_RGX = sys.argv[1]
+ del sys.argv[1]
+ testlib.unittest_main(defaultTest='suite')
+
+
diff --git a/test/func_test_rpython.py b/test/func_test_rpython.py
new file mode 100644
index 0000000..2363c92
--- /dev/null
+++ b/test/func_test_rpython.py
@@ -0,0 +1,114 @@
+# Copyright (c) 2007 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.,
+# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+"""functional/non regression tests for the rpython mode of pylint
+"""
+
+import unittest
+import sys
+import re
+import new
+from os import linesep
+from os.path import exists
+
+from logilab.common import testlib
+
+from utils import get_tests_info, fix_path, TestReporter
+
+from pylint.lint import PyLinter
+from pylint import checkers
+
+test_reporter = TestReporter()
+linter = PyLinter()
+linter.set_reporter(test_reporter)
+linter.config.persistent = 0
+checkers.initialize(linter)
+linter.enable_checkers(['rpython'], True)
+
+from func_test import ulines, LintTestUsingFile
+
+class RLintTestUsingFile(LintTestUsingFile):
+ package = 'rpythoninput'
+ linter = linter
+ def setUp(self):
+ if sys.version_info[:2] != (2, 4):
+ self.skip('only python 2.4 supported for now')
+
+ def test_functionality(self):
+ tocheck = ['rpythoninput/' + self.module + '.py']
+ if self.depends:
+ tocheck += ['rpythoninput/%s' % name for name, file in self.depends]
+ self._test(tocheck)
+
+
+class TestTests(testlib.TestCase):
+ """check that all testable messages have been checked"""
+ def setUp(self):
+ if sys.version_info[:2] != (2, 4):
+ self.skip('only python 2.4 supported for now')
+
+ def test(self):
+ # skip rpython checker messages
+ missing = [msgid for msgid in linter._messages.keys()
+ if msgid[1:3] == '12' and not msgid in test_reporter.message_ids]
+ self.assertEqual(missing, [])
+
+def make_tests(filter_rgx):
+ """generate tests classes from test info
+
+ return the list of generated test classes
+ """
+ if filter_rgx:
+ is_to_run = re.compile(filter_rgx).match
+ else:
+ is_to_run = lambda x: 1
+ tests = []
+ for module_file, messages_file in get_tests_info('func_', '.py',
+ 'rpythoninput', 'rpythonmessages'):
+ if not is_to_run(module_file):
+ continue
+ base = module_file.replace('func_', '').replace('.py', '')
+ dependancies = get_tests_info(base, '.py')
+
+ class LintTestUsingFileTC(RLintTestUsingFile):
+ module = module_file.replace('.py', '')
+ output = exists(messages_file + '2') and (messages_file + '2') or messages_file
+ depends = dependancies or None
+ tests.append(LintTestUsingFileTC)
+
+ if not filter_rgx:
+ # test all features are tested :)
+ tests.append(TestTests)
+
+ return tests
+
+FILTER_RGX = None
+MODULES_ONLY = False
+
+def suite():
+ return unittest.TestSuite([unittest.makeSuite(test)
+ for test in make_tests(FILTER_RGX)])
+
+if __name__=='__main__':
+ if '-m' in sys.argv:
+ MODULES_ONLY = True
+ sys.argv.remove('-m')
+
+ if len(sys.argv) > 1:
+ FILTER_RGX = sys.argv[1]
+ del sys.argv[1]
+ testlib.unittest_main(defaultTest='suite')
+
+
diff --git a/test/func_test_sample_config.py b/test/func_test_sample_config.py
new file mode 100644
index 0000000..ec471cc
--- /dev/null
+++ b/test/func_test_sample_config.py
@@ -0,0 +1,31 @@
+# Copyright (c) 2002-2004 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.,
+# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+"""functional tests using the sample configuration file, should behave exactly
+as with the default configuration
+"""
+
+from func_test import *
+
+from os.path import join
+
+import pylint
+sample_config = join(pylint.__path__[0], 'examples', 'pylintrc')
+linter.load_file_configuration(sample_config)
+linter.global_set_option('required-attributes', ('__revision__',))
+linter.set_reporter(test_reporter)
+
+if __name__=='__main__':
+ unittest.main(defaultTest='suite')
diff --git a/test/genbuiltins.py b/test/genbuiltins.py
new file mode 100644
index 0000000..c1a433f
--- /dev/null
+++ b/test/genbuiltins.py
@@ -0,0 +1,63 @@
+from os import remove
+from glob import glob
+
+code = """
+import os
+
+def function():
+ retval = %s
+ os.write(2, str(retval) + '\\n')
+
+def entry_point(argv):
+ function()
+ return 0
+
+def target(*args):
+ return entry_point, None
+"""
+
+msg = "E: 5:function: Using unavailable builtin '%s'"
+
+checks = [
+ 'set([1, 2, 3])', 'frozenset()',
+ 'dict()', 'unicode("f")', "complex(3, 4)",
+ 'file("foo.txt")', 'open("foo.txt")',
+
+ 'super(str)', 'object()',
+
+ 'raw_input()', 'input()', 'buffer()',
+
+ 'vars()', 'locals()', 'globals()', 'dir()',
+
+ 'getattr(function, "bar")', 'delattr(function, "bar")', 'setattr(function, "bar", "baz")',
+ 'staticmethod(function)', 'classmethod(function)', 'property(function)',
+
+ "reduce(function, [])", "filter(function, [])", 'map(function, [])',
+ 'sum(xrange(4))', 'enumerate(range(5))',
+ 'sorted(range(5))', 'reversed(range(5))',
+
+ 'callable(function)', 'issubclass(function, str)',
+
+ 'reload(module)',
+
+ 'eval("a == b")', 'compile("a==b", "?", "eval")', 'execfile("foo.py")',
+
+ 'id(function)', 'help(function)',
+
+ 'intern("foo")', 'round(3.4)', 'iter([])',
+ ]
+
+
+for filename in glob('rpythoninput/func_nobuiltin_*'):
+ remove(filename)
+for filename in glob('rpythonmessages/func_nobuiltin_*'):
+ remove(filename)
+for check in checks:
+ builtin, _ = check.split('(', 1)
+ if builtin == 'isinstance':
+ builtin = 'basestring'
+ basename = 'func_nobuiltin_%s' % builtin
+ print basename
+ file('rpythoninput/%s.py' % basename, 'w').write(code % check)
+ file('rpythonmessages/%s.txt' % basename, 'w').write(msg % builtin)
+print 'generated %s test' % len(checks)
diff --git a/test/input/__init__.py b/test/input/__init__.py
new file mode 100644
index 0000000..60e92b7
--- /dev/null
+++ b/test/input/__init__.py
@@ -0,0 +1 @@
+"""test"""
diff --git a/test/input/func___future___import_not_first_stmt.py b/test/input/func___future___import_not_first_stmt.py
new file mode 100644
index 0000000..2051a7b
--- /dev/null
+++ b/test/input/func___future___import_not_first_stmt.py
@@ -0,0 +1,5 @@
+"""a docstring"""
+
+__revision__ = 1
+from __future__ import generators
+
diff --git a/test/input/func___name___access.py b/test/input/func___name___access.py
new file mode 100644
index 0000000..25aed5c
--- /dev/null
+++ b/test/input/func___name___access.py
@@ -0,0 +1,21 @@
+# pylint: disable-msg=R0903,W0142
+"""test access to __name__ gives undefined member on new/old class instances
+but not on new/old class object
+"""
+
+__revision__ = 1
+
+class Aaaa:
+ """old class"""
+ def __init__(self):
+ print self.__name__
+ print self.__class__.__name__
+class NewClass(object):
+ """new class"""
+
+ def __new__(cls, *args, **kwargs):
+ print 'new', cls.__name__
+ return object.__new__(cls, *args, **kwargs)
+
+ def __init__(self):
+ print 'init', self.__name__
diff --git a/test/input/func_attrs_definition_order.py b/test/input/func_attrs_definition_order.py
new file mode 100644
index 0000000..eda02e1
--- /dev/null
+++ b/test/input/func_attrs_definition_order.py
@@ -0,0 +1,15 @@
+# pylint: disable-msg=R0903
+"""yo"""
+
+__revision__ = '$I$'
+
+class Aaaa:
+ """class with attributes defined in wrong order"""
+ def __init__(self):
+ var1 = self._var2
+ self._var2 = 3
+ print var1
+
+class Bbbb(object):
+ """hop"""
+ __revision__ = __revision__ # no problemo marge
diff --git a/test/input/func_backtick_deprecated.py b/test/input/func_backtick_deprecated.py
new file mode 100644
index 0000000..bca85cc
--- /dev/null
+++ b/test/input/func_backtick_deprecated.py
@@ -0,0 +1,4 @@
+"""W0333: flag backtick as deprecated"""
+
+__revision__ = None
+print `1`
diff --git a/test/input/func_bad_assigment_to_exception_var.py b/test/input/func_bad_assigment_to_exception_var.py
new file mode 100644
index 0000000..b404cb3
--- /dev/null
+++ b/test/input/func_bad_assigment_to_exception_var.py
@@ -0,0 +1,31 @@
+# pylint:disable-msg=C0103
+"""ho ho ho"""
+__revision__ = 'toto'
+
+import sys
+
+e = 1
+e2 = 'yo'
+e3 = None
+try:
+ raise e, 'toto'
+except Exception, ex:
+ print ex
+ _, _, tb = sys.exc_info()
+ raise e2
+
+
+def func():
+ """bla bla bla"""
+ raise e3
+
+def reraise():
+ """reraise a catched exception instance"""
+ try:
+ raise Exception()
+ except Exception, exc:
+ print exc
+ raise exc
+
+raise e3
+
diff --git a/test/input/func_base_stmt_without_effect.py b/test/input/func_base_stmt_without_effect.py
new file mode 100644
index 0000000..8eb8736
--- /dev/null
+++ b/test/input/func_base_stmt_without_effect.py
@@ -0,0 +1,28 @@
+"""
+ 'W0104': ('Statement seems to have no effect',
+ 'Used when a statement doesn\'t have (or at least seems to) \
+ any effect.'),
+ 'W0105': ('String statement has no effect',
+ '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': ('Unnecessary semi-column',
+ 'Used when a statement is endend by a semi-colon (";"), which \
+ isn\'t necessary (that\'s python, not C ;)'),
+"""
+
+__revision__ = ''
+
+__revision__
+
+__revision__ <= 1
+
+__revision__.lower() # ok
+
+[i for i in __revision__] # ko
+
+
+"""inline doc string should use a separated message"""
+
+__revision__.lower(); # unnecessary ;
diff --git a/test/input/func_base_useless_pass.py b/test/input/func_base_useless_pass.py
new file mode 100644
index 0000000..67c39fa
--- /dev/null
+++ b/test/input/func_base_useless_pass.py
@@ -0,0 +1,9 @@
+"""W0107: unnecessary pass statement
+"""
+__revision__ = None
+
+try:
+ A = 2
+except ValueError:
+ print A
+ pass
diff --git a/test/input/func_block_disable_msg.py b/test/input/func_block_disable_msg.py
new file mode 100644
index 0000000..5253411
--- /dev/null
+++ b/test/input/func_block_disable_msg.py
@@ -0,0 +1,1017 @@
+# pylint: disable-msg=C0302
+"""pylint option block-disable-msg"""
+__revision__ = None
+
+class Foo(object):
+ """block-disable-msg test"""
+
+ def __init__(self):
+ pass
+
+ def meth1(self, arg):
+ """this issues a message"""
+ print self
+
+ def meth2(self, arg):
+ """and this one not"""
+ # pylint: disable-msg=W0613
+ print self\
+ + "foo"
+
+ def meth3(self):
+ """test one line disabling"""
+ # no error
+ print self.bla # pylint: disable-msg=E1101
+ # error
+ print self.blop
+
+ def meth4(self):
+ """test re-enabling"""
+ # pylint: disable-msg=E1101
+ # no error
+ print self.bla
+ print self.blop
+ # pylint: enable-msg=E1101
+ # error
+ print self.blip
+
+ def meth5(self):
+ """test IF sub-block re-enabling"""
+ # pylint: disable-msg=E1101
+ # no error
+ print self.bla
+ if self.blop:
+ # pylint: enable-msg=E1101
+ # error
+ print self.blip
+ else:
+ # no error
+ print self.blip
+ # no error
+ print self.blip
+
+ def meth6(self):
+ """test TRY/EXCEPT sub-block re-enabling"""
+ # pylint: disable-msg=E1101
+ # no error
+ print self.bla
+ try:
+ # pylint: enable-msg=E1101
+ # error
+ print self.blip
+ except UndefinedName: # pylint: disable-msg=E0602
+ # no error
+ print self.blip
+ # no error
+ print self.blip
+
+ def meth7(self):
+ """test one line block opening disabling"""
+ if self.blop: # pylint: disable-msg=E1101
+ # error
+ print self.blip
+ else:
+ # error
+ print self.blip
+ # error
+ print self.blip
+
+
+ def meth8(self):
+ """test late disabling"""
+ # error
+ print self.blip
+ # pylint: disable-msg=E1101
+ # no error
+ print self.bla
+ print self.blop
+
+ def meth9(self):
+ """test re-enabling right after a block with whitespace"""
+ eris = 5
+
+ if eris:
+ print ("In block")
+
+ # pylint: disable-msg=E1101
+ # no error
+ print self.bla
+ print self.blu
+ # pylint: enable-msg=E1101
+ # error
+ print self.blip
+
+class ClassLevelMessage(object):
+ """should'nt display to much attributes/not enough methods messages
+ """
+ # pylint: disable-msg=R0902,R0903
+
+ def __init__(self):
+ self.attr1 = 1
+ self.attr2 = 1
+ self.attr3 = 1
+ self.attr4 = 1
+ self.attr5 = 1
+ self.attr6 = 1
+ self.attr7 = 1
+ self.attr8 = 1
+ self.attr9 = 1
+ self.attr0 = 1
+
+ def too_complex_but_thats_ok(self, attr1, attr2):
+ """THIS Method has too much branchs and returns but i don't care
+ """
+ # pylint: disable-msg=R0912,R0911
+ try:
+ attr3 = attr1+attr2
+ except ValueError:
+ attr3 = None
+ except:
+ return 'duh', self
+ if attr1:
+ for i in attr1:
+ if attr2:
+ return i
+ else:
+ return 'duh'
+ elif attr2:
+ for i in attr2:
+ if attr2:
+ return i
+ else:
+ return 'duh'
+ else:
+ for i in range(15):
+ if attr3:
+ return i
+ else:
+ return 'doh'
+ return None
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+print 'hop, too many lines but i don\'t care'
diff --git a/test/input/func_class_access_protected_members.py b/test/input/func_class_access_protected_members.py
new file mode 100644
index 0000000..f588953
--- /dev/null
+++ b/test/input/func_class_access_protected_members.py
@@ -0,0 +1,30 @@
+# pylint: disable-msg=R0903
+"""test external access to protected class members"""
+
+__revision__ = ''
+
+class MyClass:
+ """class docstring"""
+ _cls_protected = 5
+
+ def __init__(self, other):
+ """init"""
+ self._protected = 1
+ self.public = other
+
+
+ def test(self):
+ """test"""
+ self._protected += self._cls_protected
+ print self.public._haha
+
+ def clsmeth(cls):
+ """this is ok"""
+ print cls._cls_protected
+ clsmeth = classmethod(clsmeth)
+
+INST = MyClass()
+print INST.public
+print INST._protected
+print INST._cls_protected
+
diff --git a/test/input/func_class_members.py b/test/input/func_class_members.py
new file mode 100644
index 0000000..8b2e969
--- /dev/null
+++ b/test/input/func_class_members.py
@@ -0,0 +1,31 @@
+# pylint: disable-msg=R0903
+"""test class members"""
+
+__revision__ = ''
+
+class MyClass:
+ """class docstring"""
+
+ def __init__(self):
+ """init"""
+ self.correct = 1
+
+ def test(self):
+ """test"""
+ self.correct += 2
+ self.incorrect += 2
+ self.nonexistent1.truc()
+ self.nonexistent2[1] = 'hehe'
+
+class XYZMixin:
+ """access to undefined members should be ignored in mixin classes by
+ default
+ """
+ def __init__(self):
+ print self.nonexistent
+
+
+class NewClass(object):
+ """use object.__setattr__"""
+ def __init__(self):
+ self.__setattr__('toto', 'tutu')
diff --git a/test/input/func_continue_not_in_loop.py b/test/input/func_continue_not_in_loop.py
new file mode 100644
index 0000000..4186aa5
--- /dev/null
+++ b/test/input/func_continue_not_in_loop.py
@@ -0,0 +1,14 @@
+"""this module produces a SyntaxError at execution time"""
+
+__revision__ = None
+
+def run():
+ """simple function"""
+ if True:
+ continue
+ else:
+ break
+
+if __name__ == '__main__':
+ run()
+
diff --git a/test/input/func_dangerous_default.py b/test/input/func_dangerous_default.py
new file mode 100644
index 0000000..0bf8727
--- /dev/null
+++ b/test/input/func_dangerous_default.py
@@ -0,0 +1,17 @@
+"""docstring"""
+
+__revision__ = ''
+
+HEHE = {}
+
+def function1(value = []):
+ """docstring"""
+ print value
+
+def function2(value = HEHE):
+ """docstring"""
+ print value
+
+def function3(value):
+ """docstring"""
+ print value
diff --git a/test/input/func_docstring.py b/test/input/func_docstring.py
new file mode 100644
index 0000000..7f28672
--- /dev/null
+++ b/test/input/func_docstring.py
@@ -0,0 +1,48 @@
+# pylint: disable-msg=R0201
+
+__revision__ = ''
+
+def function1(value):
+ # missing docstring
+ print value
+
+def function2(value):
+ """docstring"""
+ print value
+
+def function3(value):
+ """docstring"""
+ print value
+
+class AAAA:
+ # missing docstring
+
+## class BBBB:
+## # missing docstring
+## pass
+
+## class CCCC:
+## """yeah !"""
+## def method1(self):
+## pass
+
+## def method2(self):
+## """ yeah !"""
+## pass
+
+ def method1(self):
+ pass
+
+ def method2(self):
+ """ yeah !"""
+ pass
+
+ def __init__(self):
+ pass
+
+class DDDD(AAAA):
+ """yeah !"""
+
+ def __init__(self):
+ AAAA.__init__(self)
+
diff --git a/test/input/func_dotted_ancestor.py b/test/input/func_dotted_ancestor.py
new file mode 100644
index 0000000..2d2f571
--- /dev/null
+++ b/test/input/func_dotted_ancestor.py
@@ -0,0 +1,11 @@
+"""bla"""
+
+__revision__ = 'yo'
+
+
+from input import func_w0233
+
+class Aaaa(func_w0233.AAAA):
+ """test dotted name in ancestors"""
+ def __init__(self):
+ func_w0233.AAAA.__init__(self)
diff --git a/test/input/func_e0011.py b/test/input/func_e0011.py
new file mode 100644
index 0000000..f2bb592
--- /dev/null
+++ b/test/input/func_e0011.py
@@ -0,0 +1,5 @@
+# pylint:bouboule=1
+"""check unknown option
+"""
+__revision__ = 1
+
diff --git a/test/input/func_e0012.py b/test/input/func_e0012.py
new file mode 100644
index 0000000..11ef99f
--- /dev/null
+++ b/test/input/func_e0012.py
@@ -0,0 +1,5 @@
+# pylint:enable-msg=W04044
+"""check unknown option
+"""
+__revision__ = 1
+
diff --git a/test/input/func_e0101.py b/test/input/func_e0101.py
new file mode 100644
index 0000000..9158041
--- /dev/null
+++ b/test/input/func_e0101.py
@@ -0,0 +1,29 @@
+# pylint: disable-msg=R0903
+"""test __init__ return
+"""
+
+__revision__ = 'yo'
+
+class MyClass:
+ """dummy class"""
+
+ def __init__(self):
+ return 1
+
+class MyClass2:
+ """dummy class"""
+
+ def __init__(self):
+ return
+
+class MyClass3:
+ """dummy class"""
+
+ def __init__(self):
+ return None
+
+class MyClass4:
+ """dummy class"""
+
+ def __init__(self):
+ yield None
diff --git a/test/input/func_e0203.py b/test/input/func_e0203.py
new file mode 100644
index 0000000..d51de0d
--- /dev/null
+++ b/test/input/func_e0203.py
@@ -0,0 +1,19 @@
+"""check for method without self as first argument
+"""
+
+__revision__ = 0
+
+
+class Abcd:
+ """dummy class"""
+ def __init__(self):
+ pass
+
+ def abcd(yoo):
+ """another test"""
+
+ abcd = classmethod(abcd)
+
+ def edf(self):
+ """justo ne more method"""
+ print 'yapudju in', self
diff --git a/test/input/func_e0204.py b/test/input/func_e0204.py
new file mode 100644
index 0000000..ecbc51c
--- /dev/null
+++ b/test/input/func_e0204.py
@@ -0,0 +1,19 @@
+"""check for method without self as first argument
+"""
+
+__revision__ = 0
+
+
+class Abcd:
+ """dummy class"""
+
+ def __init__(truc):
+ """method without self"""
+ print 1
+
+ def abdc(yoo):
+ """another test"""
+ print yoo
+ def edf(self):
+ """just another method"""
+ print 'yapudju in', self
diff --git a/test/input/func_e0205.py b/test/input/func_e0205.py
new file mode 100644
index 0000000..2bb1089
--- /dev/null
+++ b/test/input/func_e0205.py
@@ -0,0 +1,17 @@
+# pylint: disable-msg=R0903
+"""check method hidding ancestor attribute
+"""
+
+__revision__ = ''
+
+class Abcd:
+ """dummy"""
+ def __init__(self):
+ self.abcd = 1
+
+class Cdef(Abcd):
+ """dummy"""
+ def abcd(self):
+ """test
+ """
+ print self
diff --git a/test/input/func_e0206.py b/test/input/func_e0206.py
new file mode 100644
index 0000000..af32fd8
--- /dev/null
+++ b/test/input/func_e0206.py
@@ -0,0 +1,20 @@
+# pylint: disable-msg=R0903
+"""check for interface which are not classes"""
+
+__revision__ = None
+
+class Abcd:
+ """dummy"""
+ __implements__ = __revision__
+
+ def __init__(self):
+ self.attr = None
+
+class Cdef:
+ """dummy"""
+ __implements__ = (__revision__, Abcd)
+
+ def __init__(self):
+ pass
+
+
diff --git a/test/input/func_e0214.py b/test/input/func_e0214.py
new file mode 100644
index 0000000..8a4ad32
--- /dev/null
+++ b/test/input/func_e0214.py
@@ -0,0 +1,18 @@
+"""mcs test"""
+
+__revision__ = 1
+
+class MetaClass(type):
+ """a very intersting metaclass"""
+ def __new__(mcs, name, bases, cdict):
+ print mcs, name, bases, cdict
+ return type.__new__(mcs, name, bases, cdict)
+
+ def whatever(self):
+ """should have mcs has first arg"""
+ print self
+
+ def whatever_really(hop):
+ """could have anything has first arg"""
+ print hop
+ whatever_really = staticmethod(whatever_really)
diff --git a/test/input/func_e0601.py b/test/input/func_e0601.py
new file mode 100644
index 0000000..b8673df
--- /dev/null
+++ b/test/input/func_e0601.py
@@ -0,0 +1,9 @@
+"""test local variable used before assigment
+"""
+
+__revision__ = 0
+
+def function():
+ """dummy"""
+ print aaaa
+ aaaa = 1
diff --git a/test/input/func_empty_module.py b/test/input/func_empty_module.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/input/func_empty_module.py
diff --git a/test/input/func_exceptions_raise_type_error.py b/test/input/func_exceptions_raise_type_error.py
new file mode 100644
index 0000000..8c414b6
--- /dev/null
+++ b/test/input/func_exceptions_raise_type_error.py
@@ -0,0 +1,14 @@
+"""
+'E0702': Raising an %s while only classes, instances or string are allowed
+
+Used when something which is neither a class, an instance or a string is
+raised (i.e. a `TypeError` will be raised).
+"""
+
+__revision__ = 1
+
+if __revision__:
+ raise 1
+
+if __revision__:
+ raise None
diff --git a/test/input/func_f0001.py b/test/input/func_f0001.py
new file mode 100644
index 0000000..af6de24
--- /dev/null
+++ b/test/input/func_f0001.py
@@ -0,0 +1,4 @@
+"""test astng error
+"""
+import whatever
+__revision__ = None
diff --git a/test/input/func_f0401.py b/test/input/func_f0401.py
new file mode 100644
index 0000000..f8443fa
--- /dev/null
+++ b/test/input/func_f0401.py
@@ -0,0 +1,9 @@
+"""tset failed import
+"""
+
+__revision__ = 0
+
+def function():
+ """yo"""
+ from tutu import toto
+ print toto
diff --git a/test/input/func_fixme.py b/test/input/func_fixme.py
new file mode 100644
index 0000000..0cfe2bd
--- /dev/null
+++ b/test/input/func_fixme.py
@@ -0,0 +1,9 @@
+"""docstring"""
+
+__revision__ = ''
+
+# FIXME: beep
+
+def function():
+ '''XXX:bop'''
+
diff --git a/test/input/func_format.py b/test/input/func_format.py
new file mode 100644
index 0000000..58649e9
--- /dev/null
+++ b/test/input/func_format.py
@@ -0,0 +1,64 @@
+# pylint:disable-msg=C0103,W0104,W0105
+"""Check format
+"""
+__revision__ = ''
+
+notpreceded= 1
+notfollowed =1
+notfollowed <=1
+
+correct = 1
+correct >= 1
+
+def func(arg, arg2):
+ """test named argument
+ """
+ func(arg=arg+1,
+ arg2=arg2-arg)
+
+aaaa,bbbb = 1,2
+aaaa |= bbbb
+aaaa &= bbbb
+
+
+if aaaa: pass
+else:
+ aaaa,bbbb = 1,2
+ aaaa,bbbb = bbbb,aaaa
+
+bbbb = (1,2,3)
+
+aaaa = bbbb[1:]
+aaaa = bbbb[:1]
+aaaa = bbbb[:]
+
+aaaa = {aaaa:bbbb}
+
+
+# allclose(x,y) uses |x-y|<ATOL+RTOL*|y|
+"""docstring,should not match
+isn't it:yes!
+a=b
+"""
+aaaa = 'multiple lines\
+string,hehehe'
+
+
+boo = 2 # allclose(x,y) uses |x-y|<ATOL+RTOL*|y|
+
+def other(funky):
+ """yo, test formatted result with indentation"""
+ funky= funky+2
+
+html = """<option value="=">ist genau gleich</option>
+yo+=4
+"""
+html2 = """<option value='='>ist genau gleich</option>
+yo+=4
+"""
+
+func('''<body>Hello
+</body>''')
+
+assert boo <= 10, "Note is %.2f. Either you cheated, or pylint's \
+broken!" % boo
diff --git a/test/input/func_genexpr_var_scope_py24.py b/test/input/func_genexpr_var_scope_py24.py
new file mode 100644
index 0000000..2b6faf3
--- /dev/null
+++ b/test/input/func_genexpr_var_scope_py24.py
@@ -0,0 +1,6 @@
+"""test name defined in generator expression are not available
+outside the genexpr scope
+"""
+
+__revision__ = list(n for n in range(10))
+print n
diff --git a/test/input/func_globals.py b/test/input/func_globals.py
new file mode 100644
index 0000000..f7e6c65
--- /dev/null
+++ b/test/input/func_globals.py
@@ -0,0 +1,40 @@
+"""
+'W0601': ('global variable %s undefined at the module level',
+ 'Used when a variable is defined through the "global" statement \
+ but the variable is not defined in the module scope.'),
+'W0602': ('Using global for %s but no assigment is done',
+ 'Used when a variable is defined through the "global" statement \
+ but no assigment to this variable is done.'),
+'W0603': ('Using the global statement', # W0121
+ '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
+ 'Used when you use the "global" statement at the module level \
+ since it has no effect'),
+"""
+
+__revision__ = ''
+
+CONSTANT = 1
+
+def fix_contant(value):
+ """all this is ok, but try not using global ;)"""
+ global CONSTANT
+ print CONSTANT
+ CONSTANT = value
+global CSTE # useless
+print CSTE # ko
+
+def other():
+ """global behaviour test"""
+ global HOP
+ print HOP # ko
+
+other()
+
+
+def define_constant():
+ """ok but somevar is not defined at the module scope"""
+ global somevar
+ somevar = 2
diff --git a/test/input/func_i0010.py b/test/input/func_i0010.py
new file mode 100644
index 0000000..11aafeb
--- /dev/null
+++ b/test/input/func_i0010.py
@@ -0,0 +1,3 @@
+# pylint: errors-only
+"""errors-only is not usable as an inline option"""
+__revision__ = None
diff --git a/test/input/func_i0011.py b/test/input/func_i0011.py
new file mode 100644
index 0000000..d8202f3
--- /dev/null
+++ b/test/input/func_i0011.py
@@ -0,0 +1,5 @@
+# pylint:disable-msg=W0404
+"""check warning on local disabling
+"""
+__revision__ = 1
+
diff --git a/test/input/func_i0012.py b/test/input/func_i0012.py
new file mode 100644
index 0000000..bf52138
--- /dev/null
+++ b/test/input/func_i0012.py
@@ -0,0 +1,5 @@
+# pylint:enable-msg=W0404
+"""check warning on local enabling
+"""
+__revision__ = 1
+
diff --git a/test/input/func_i0013.py b/test/input/func_i0013.py
new file mode 100644
index 0000000..63ff37c
--- /dev/null
+++ b/test/input/func_i0013.py
@@ -0,0 +1,8 @@
+# pylint: disable-all
+"""disable-all is usable as an inline option"""
+
+# no warning should be issued
+try:
+ import this
+except:
+ pass
diff --git a/test/input/func_indent.py b/test/input/func_indent.py
new file mode 100644
index 0000000..44eb429
--- /dev/null
+++ b/test/input/func_indent.py
@@ -0,0 +1,22 @@
+"""docstring"""
+__revision__ = '$Id: func_indent.py,v 1.4 2003-10-17 21:59:31 syt Exp $'
+
+def totoo():
+ """docstring"""
+ print 'malindented'
+
+def tutuu():
+ """docstring"""
+ print 'good indentation'
+
+def titii():
+ """also malindented"""
+
+def tataa(kdict):
+ """blank line unindented"""
+ for key in ['1', '2', '3']:
+ key = key.lower()
+
+ if kdict.has_key(key):
+ del kdict[key]
+
diff --git a/test/input/func_init_vars.py b/test/input/func_init_vars.py
new file mode 100644
index 0000000..9d89505
--- /dev/null
+++ b/test/input/func_init_vars.py
@@ -0,0 +1,46 @@
+"""Checks that class variables are seen as inherited !
+"""
+
+__revision__ = ''
+
+
+class MyClass:
+ """Inherits from nothing
+ """
+
+ def __init__(self):
+ self.var = {}
+
+ def met(self):
+ """Checks that base_var is seen as defined outside '__init__'
+ """
+ self.var[1] = 'one'
+ self.base_var = 'one'
+ print self.base_var, self.var
+
+ def met2(self):
+ """dummy method"""
+ print self
+class MySubClass(MyClass):
+ """Inherits from MyClass
+ """
+ class_attr = 1
+
+ def __init__(self):
+ MyClass.__init__(self)
+ self.var2 = 2
+ print self.__doc__
+ print self.__dict__
+ print self.__class__
+
+ def met2(self):
+ """Checks that var is seen as defined outside '__init__'
+ """
+ self.var[1] = 'one'
+ self.var2 += 1
+ print self.class_attr
+
+if __name__ == '__main__':
+ OBJ = MyClass()
+ OBJ.met()
+
diff --git a/test/input/func_interfaces.py b/test/input/func_interfaces.py
new file mode 100644
index 0000000..2006074
--- /dev/null
+++ b/test/input/func_interfaces.py
@@ -0,0 +1,99 @@
+# pylint:disable-msg=R0201
+"""docstring"""
+__revision__ = ''
+
+class Interface:
+ """base class for interfaces"""
+
+class IMachin(Interface):
+ """docstring"""
+ def truc(self):
+ """docstring"""
+
+ def troc(self, argument):
+ """docstring"""
+
+class Correct1:
+ """docstring"""
+ __implements__ = IMachin
+
+ def __init__(self):
+ pass
+
+ def truc(self):
+ """docstring"""
+ pass
+
+ def troc(self, argument):
+ """docstring"""
+ pass
+
+class Correct2:
+ """docstring"""
+ __implements__ = (IMachin,)
+
+ def __init__(self):
+ pass
+
+ def truc(self):
+ """docstring"""
+ pass
+
+ def troc(self, argument):
+ """docstring"""
+ print argument
+
+class MissingMethod:
+ """docstring"""
+ __implements__ = IMachin,
+
+ def __init__(self):
+ pass
+
+ def troc(self, argument):
+ """docstring"""
+ print argument
+
+ def other(self):
+ """docstring"""
+
+class BadArgument:
+ """docstring"""
+ __implements__ = (IMachin,)
+
+ def __init__(self):
+ pass
+
+ def truc(self):
+ """docstring"""
+ pass
+
+ def troc(self):
+ """docstring"""
+ pass
+
+class InterfaceCantBeFound:
+ """docstring"""
+ __implements__ = undefined
+
+ def __init__(self):
+ """only to make pylint happier"""
+
+ def please(self):
+ """public method 1/2"""
+
+ def besilent(self):
+ """public method 2/2"""
+
+class InterfaceCanNowBeFound:
+ """docstring"""
+ __implements__ = BadArgument.__implements__ + Correct2.__implements__
+
+ def __init__(self):
+ """only to make pylint happier"""
+
+ def please(self):
+ """public method 1/2"""
+
+ def besilent(self):
+ """public method 2/2"""
diff --git a/test/input/func_method_could_be_function.py b/test/input/func_method_could_be_function.py
new file mode 100644
index 0000000..5988b68
--- /dev/null
+++ b/test/input/func_method_could_be_function.py
@@ -0,0 +1,52 @@
+# pylint: disable-msg=R0903,R0922,W0232
+"""test detection of method which could be a function"""
+
+__revision__ = None
+
+class Toto(object):
+ """bla bal abl"""
+
+ def __init__(self):
+ self.aaa = 2
+
+ def regular_method(self):
+ """this method is a real method since it access to self"""
+ self.function_method()
+
+ def function_method(self):
+ """this method isn' a real method since it doesn't need self"""
+ print 'hello'
+
+
+class Base:
+ """an abstract class"""
+
+ def __init__(self):
+ self.aaa = 2
+
+ def check(self, arg):
+ """an abstract method, could not be a function"""
+ raise NotImplementedError
+
+
+class Sub(Base):
+ """a concret class"""
+
+ def check(self, arg):
+ """a concret method, could not be a function since it need
+ polymorphism benefits
+ """
+ return arg == 0
+
+class Super:
+ """same as before without abstract"""
+ x = 1
+ def method(self):
+ """regular"""
+ print self.x
+
+class Sub1(Super):
+ """override method with need for self"""
+ def method(self):
+ """no i can not be a function"""
+ print 42
diff --git a/test/input/func_method_missing_self.py b/test/input/func_method_missing_self.py
new file mode 100644
index 0000000..5bdbc69
--- /dev/null
+++ b/test/input/func_method_missing_self.py
@@ -0,0 +1,25 @@
+"""Checks that missing self in method defs don't crash Pylint !
+"""
+
+__revision__ = ''
+
+
+class MyClass:
+ """SimpleClass
+ """
+
+ def __init__(self):
+ self.var = "var"
+
+ def met():
+ """Checks that missing self dont crash Pylint !
+ """
+
+ def correct(self):
+ """yo"""
+ self.var = "correct"
+
+if __name__ == '__main__':
+ OBJ = MyClass()
+ OBJ.met()
+
diff --git a/test/input/func_method_without_self_but_self_assignment.py b/test/input/func_method_without_self_but_self_assignment.py
new file mode 100644
index 0000000..e39266c
--- /dev/null
+++ b/test/input/func_method_without_self_but_self_assignment.py
@@ -0,0 +1,15 @@
+# pylint: disable-msg=R0903
+"""regression test: setup() leads to "unable to load module..."
+"""
+
+__revision__ = 1
+
+class Example:
+ """bla"""
+
+ def __init__(self):
+ pass
+
+ def setup():
+ "setup without self"
+ self.foo = 1
diff --git a/test/input/func_nameerror_on_string_substitution.py b/test/input/func_nameerror_on_string_substitution.py
new file mode 100644
index 0000000..be7b5c8
--- /dev/null
+++ b/test/input/func_nameerror_on_string_substitution.py
@@ -0,0 +1,8 @@
+"""pylint doesn't see the NameError in this module"""
+
+__revision__ = None
+
+MSG = "hello %s" % MSG
+
+MSG2 = ("hello %s" %
+ MSG2)
diff --git a/test/input/func_names_imported_from_module.py b/test/input/func_names_imported_from_module.py
new file mode 100644
index 0000000..caf7387
--- /dev/null
+++ b/test/input/func_names_imported_from_module.py
@@ -0,0 +1,31 @@
+#pylint: disable-msg=W0401,W0611
+"""check unexistant names imported are reported"""
+
+__revision__ = None
+
+import logilab.common.tutu
+from logilab.common import toto
+toto.yo()
+
+from logilab.common import modutils
+modutils.nonexistant_function()
+modutils.another.nonexistant.function()
+print logilab.common.modutils.yo
+
+import sys
+print >> sys.stdout, 'hello world'
+print >> sys.stdoout, 'bye bye world'
+
+
+import re
+re.finditer('*', 'yo')
+
+from rie import *
+from re import findiiter, compiile
+
+import os
+os.environ.has_key('SOMEVAR')
+
+import exceptions
+print exceptions.__dict__
+print exceptions.__dict__.get('Exception')
diff --git a/test/input/func_newstyle___slots__.py b/test/input/func_newstyle___slots__.py
new file mode 100644
index 0000000..78909c0
--- /dev/null
+++ b/test/input/func_newstyle___slots__.py
@@ -0,0 +1,17 @@
+# pylint: disable-msg=R0903
+"""test __slots__ on old style class"""
+
+__revision__ = 1
+
+class OkOk(object):
+ """correct usage"""
+ __slots__ = ('a', 'b')
+
+class HaNonNonNon:
+ """bad usage"""
+ __slots__ = ('a', 'b')
+
+ def __init__(self):
+ pass
+
+__slots__ = 'hop' # pfff
diff --git a/test/input/func_newstyle_exceptions.py b/test/input/func_newstyle_exceptions.py
new file mode 100644
index 0000000..273624f
--- /dev/null
+++ b/test/input/func_newstyle_exceptions.py
@@ -0,0 +1,35 @@
+# pylint: disable-msg=C0103
+"""test pb with exceptions and old/new style classes"""
+
+__revision__ = 1
+
+class OkException(Exception):
+ """bien bien bien"""
+
+class BofException:
+ """mouais"""
+
+class NewException(object):
+ """non si py < 2.5 !"""
+
+def fonctionOk():
+ """raise"""
+ raise OkException('hop')
+
+def fonctionBof():
+ """raise"""
+ raise BofException('hop')
+
+def fonctionNew():
+ """raise"""
+ raise NewException()
+
+def fonctionBof2():
+ """raise"""
+ raise BofException, 'hop'
+
+def fonctionNew2():
+ """raise"""
+ raise NewException
+
+
diff --git a/test/input/func_newstyle_property.py b/test/input/func_newstyle_property.py
new file mode 100644
index 0000000..cdce8fa
--- /dev/null
+++ b/test/input/func_newstyle_property.py
@@ -0,0 +1,19 @@
+# pylint: disable-msg=R0903
+"""test property on old style class"""
+
+__revision__ = 1
+
+def getter(self):
+ """interesting"""
+ return self
+
+class OkOk(object):
+ """correct usage"""
+ method = property(getter, doc='hop')
+
+class HaNonNonNon:
+ """bad usage"""
+ method = property(getter, doc='hop')
+
+ def __init__(self):
+ pass
diff --git a/test/input/func_newstyle_super.py b/test/input/func_newstyle_super.py
new file mode 100644
index 0000000..e84c908
--- /dev/null
+++ b/test/input/func_newstyle_super.py
@@ -0,0 +1,22 @@
+# pylint: disable-msg=R0903
+"""check use of super"""
+__revision__ = None
+
+class Aaaa:
+ """old style"""
+ def hop(self):
+ """hop"""
+ super(Aaaa, self).hop()
+
+ def __init__(self):
+ super(Aaaa, self).__init__()
+
+class NewAaaa(object):
+ """old style"""
+ def hop(self):
+ """hop"""
+ super(NewAaaa, self).hop()
+
+ def __init__(self):
+ super(object, self).__init__()
+
diff --git a/test/input/func_noerror___future___import.py b/test/input/func_noerror___future___import.py
new file mode 100644
index 0000000..5c77516
--- /dev/null
+++ b/test/input/func_noerror___future___import.py
@@ -0,0 +1,5 @@
+"""a docstring"""
+
+from __future__ import generators
+
+__revision__ = 1
diff --git a/test/input/func_noerror___init___return_from_inner_function.py b/test/input/func_noerror___init___return_from_inner_function.py
new file mode 100644
index 0000000..30b2671
--- /dev/null
+++ b/test/input/func_noerror___init___return_from_inner_function.py
@@ -0,0 +1,13 @@
+# pylint: disable-msg=R0903
+"""#10075"""
+
+__revision__ = 1
+
+class Aaa:
+ """docstring"""
+ def __init__(self):
+ def inner_function(arg):
+ """inner docstring"""
+ return arg + 4
+ self.func = inner_function
+
diff --git a/test/input/func_noerror_access_attr_before_def_false_positive.py b/test/input/func_noerror_access_attr_before_def_false_positive.py
new file mode 100644
index 0000000..048d938
--- /dev/null
+++ b/test/input/func_noerror_access_attr_before_def_false_positive.py
@@ -0,0 +1,100 @@
+#pylint: disable-msg=C0103,R0904,R0903,W0201
+"""
+This module demonstrates a possible problem of pyLint with calling __init__ s
+from inherited classes.
+Initializations done there are not considered, which results in Error E0203 for
+self.cookedq.
+"""
+
+__revision__ = 'yo'
+
+import telnetlib
+
+class SeeTelnet(telnetlib.Telnet):
+ """
+ Extension of telnetlib.
+ """
+
+ def __init__(self, host=None, port=0):
+ """
+ Constructor.
+ When called without arguments, create an unconnected instance.
+ With a hostname argument, it connects the instance; a port
+ number is optional.
+ Parameter:
+ - host: IP address of the host
+ - port: Port number
+ """
+ telnetlib.Telnet.__init__(self, host, port)
+
+ def readUntilArray(self, matches, _=None):
+ """
+ Read until a given string is encountered or until timeout.
+ ...
+ """
+ self.process_rawq()
+ maxLength = 0
+ index = -1
+ for match in matches:
+ index += 1
+ if len(match) > maxLength:
+ maxLength = len(match)
+
+class Base(object):
+ """bla bla"""
+ dougloup_papa = None
+
+ def __init__(self):
+ self._var = False
+
+class Derived(Base):
+ """derived blabla"""
+ dougloup_moi = None
+ def Work(self):
+ """do something"""
+ # E0203 - Access to member '_var' before its definition
+ if self._var:
+ print "True"
+ else:
+ print "False"
+ self._var = True
+
+ # E0203 - Access to member 'dougloup_papa' before its definition
+ if self.dougloup_papa:
+ print 'dougloup !'
+ self.dougloup_papa = True
+ # E0203 - Access to member 'dougloup_moi' before its definition
+ if self.dougloup_moi:
+ print 'dougloup !'
+ self.dougloup_moi = True
+
+
+class QoSALConnection(object):
+ """blabla"""
+
+ _the_instance = None
+
+ def __new__(cls):
+ if cls._the_instance is None:
+ cls._the_instance = object.__new__(cls)
+ return cls._the_instance
+
+ def __init__(self):
+ pass
+
+class DefinedOutsideInit:
+ """use_attr is seen as the method defining attr because its in
+ first position
+ """
+ def __init__(self):
+ self.reset()
+
+ def use_attr(self):
+ """use and set members"""
+ if self.attr:
+ print 'hop'
+ self.attr = 10
+
+ def reset(self):
+ """reset members"""
+ self.attr = 4
diff --git a/test/input/func_noerror_base_init_vars.py b/test/input/func_noerror_base_init_vars.py
new file mode 100644
index 0000000..cc9882f
--- /dev/null
+++ b/test/input/func_noerror_base_init_vars.py
@@ -0,0 +1,36 @@
+# pylint:disable-msg=R0201
+"""Checks that class variables are seen as inherited !
+"""
+__revision__ = ''
+
+class BaseClass:
+ """A simple base class
+ """
+
+ def __init__(self):
+ self.base_var = {}
+
+ def met(self):
+ """yo"""
+ def meeting(self, with_):
+ """ye"""
+ return with_
+class MyClass(BaseClass):
+ """Inherits from BaseClass
+ """
+
+ def __init__(self):
+ BaseClass.__init__(self)
+ self.var = {}
+
+ def met(self):
+ """Checks that base_var is not seen as defined outsite '__init__'
+ """
+ self.var[1] = 'one'
+ self.base_var[1] = 'one'
+ print self.base_var, self.var
+
+if __name__ == '__main__':
+ OBJ = MyClass()
+ OBJ.met()
+
diff --git a/test/input/func_noerror_builtin_module_test.py b/test/input/func_noerror_builtin_module_test.py
new file mode 100644
index 0000000..4d6ba0a
--- /dev/null
+++ b/test/input/func_noerror_builtin_module_test.py
@@ -0,0 +1,11 @@
+"""test import from a builtin module"""
+
+__revision__ = None
+
+from math import log10
+
+def log10_2():
+ """bla bla bla"""
+ return log10(2)
+
+
diff --git a/test/input/func_noerror_classes_meth_could_be_a_function.py b/test/input/func_noerror_classes_meth_could_be_a_function.py
new file mode 100644
index 0000000..d97b6cb
--- /dev/null
+++ b/test/input/func_noerror_classes_meth_could_be_a_function.py
@@ -0,0 +1,34 @@
+# pylint: disable-msg=C0111,R0903,W0232
+"""
+#2479
+
+R0201 (formely W0212), Method could be a function shouldn't be emitted in case
+like factory method pattern
+"""
+__revision__ = 1
+
+class XAsub:
+ pass
+class XBsub(XAsub):
+ pass
+class XCsub(XAsub):
+ pass
+
+class Aimpl:
+ # disable "method could be a function" on classes which are not overriding
+ # the factory method because in that case the usage of polymorphism is not
+ # detected
+ # pylint: disable-msg=R0201
+ def makex(self):
+ return XAsub()
+
+class Bimpl(Aimpl):
+
+ def makex(self):
+ return XBsub()
+
+class Cimpl(Aimpl):
+
+ def makex(self):
+ return XCsub()
+
diff --git a/test/input/func_noerror_classes_meth_signature.py b/test/input/func_noerror_classes_meth_signature.py
new file mode 100644
index 0000000..dea3e6c
--- /dev/null
+++ b/test/input/func_noerror_classes_meth_signature.py
@@ -0,0 +1,12 @@
+# pylint: disable-msg=C0111,R0922,R0903
+"""#2485
+W0222 "Signature differs from overriden method" false positive
+"""
+__revision__ = 1
+class Super(object):
+ def method(self, param):
+ raise NotImplementedError
+
+class Sub(Super):
+ def method(self, param = 'abc'):
+ pass
diff --git a/test/input/func_noerror_classes_protected_member_access.py b/test/input/func_noerror_classes_protected_member_access.py
new file mode 100644
index 0000000..d7667b8
--- /dev/null
+++ b/test/input/func_noerror_classes_protected_member_access.py
@@ -0,0 +1,24 @@
+"""
+#3123: W0212 false positive on static method
+"""
+__revision__ = 1
+
+class A3123:
+ """oypuee"""
+ _protected = 1
+ def __init__(self):
+ pass
+
+
+ def cmeth(cls, val):
+ """set protected member"""
+ cls._protected = +val
+
+ cmeth = classmethod(cmeth)
+
+ def smeth(val):
+ """set protected member"""
+ A3123._protected += val
+
+ smeth = staticmethod(smeth)
+
diff --git a/test/input/func_noerror_defined_and_used_on_same_line.py b/test/input/func_noerror_defined_and_used_on_same_line.py
new file mode 100644
index 0000000..17e4557
--- /dev/null
+++ b/test/input/func_noerror_defined_and_used_on_same_line.py
@@ -0,0 +1,23 @@
+#pylint: disable-msg=C0111,C0321
+"""pylint complains about 'index' being used before definition"""
+
+__revision__ = None
+
+print [index
+ for index in range(10)]
+
+
+FILTER_FUNC = lambda x: not x
+
+def func(xxx): return xxx
+
+def func2(xxx): return xxx + func2(1)
+
+import sys; print sys.exc_info( )
+
+for i in range(10): print i
+
+j = 4; LAMB = lambda x: x+j
+
+FUNC4 = lambda a, b : a != b
+FUNC3 = lambda (a, b) : a != b
diff --git a/test/input/func_noerror_defined_and_used_on_same_line_py24.py b/test/input/func_noerror_defined_and_used_on_same_line_py24.py
new file mode 100644
index 0000000..8032a8f
--- /dev/null
+++ b/test/input/func_noerror_defined_and_used_on_same_line_py24.py
@@ -0,0 +1,7 @@
+#pylint: disable-msg=C0111,C0321
+"""pylint complains about 'index' being used before definition"""
+
+__revision__ = None
+
+print (index
+ for index in range(10))
diff --git a/test/input/func_noerror_e1101_13784.py b/test/input/func_noerror_e1101_13784.py
new file mode 100644
index 0000000..b247b44
--- /dev/null
+++ b/test/input/func_noerror_e1101_13784.py
@@ -0,0 +1,15 @@
+"""cf #13784
+"""
+
+__revision__ = None
+
+def no_conjugate_member(magic_flag):
+ """should not raise E1101 on something.conjugate"""
+ if magic_flag:
+ something = 1.0
+ else:
+ something = 1.0j
+ if isinstance(something, float):
+ return something
+ return something.conjugate()
+
diff --git a/test/input/func_noerror_e1101_but_getattr.py b/test/input/func_noerror_e1101_but_getattr.py
new file mode 100644
index 0000000..143ddc0
--- /dev/null
+++ b/test/input/func_noerror_e1101_but_getattr.py
@@ -0,0 +1,23 @@
+"""don't want E1101 if __getattr__ is defined"""
+
+__revision__ = None
+
+class MyString:
+ """proxied string"""
+
+ def __init__(self, string):
+ self.string = string
+
+ def __getattr__(self, attr):
+ return getattr(self.string, attr)
+
+ def lower(self):
+ """string.lower"""
+ return self.string.lower()
+
+ def upper(self):
+ """string.upper"""
+ return self.string.upper()
+
+MYSTRING = MyString("abc")
+print MYSTRING.title()
diff --git a/test/input/func_noerror_encoding.py b/test/input/func_noerror_encoding.py
new file mode 100644
index 0000000..2e945a5
--- /dev/null
+++ b/test/input/func_noerror_encoding.py
@@ -0,0 +1,6 @@
+# -*- coding: ISO-8859-1 -*-
+""" check correct encoding declaration
+"""
+
+__revision__ = 'éééé'
+
diff --git a/test/input/func_noerror_except_pass.py b/test/input/func_noerror_except_pass.py
new file mode 100644
index 0000000..d032f2a
--- /dev/null
+++ b/test/input/func_noerror_except_pass.py
@@ -0,0 +1,11 @@
+"""
+#3205: W0704 (except doesn't do anything) false positive if some statements
+follow a "pass"
+"""
+__revision__ = None
+
+try:
+ A = 2
+except ValueError:
+ pass # pylint: disable-msg=W0107
+ print A
diff --git a/test/input/func_noerror_exception.py b/test/input/func_noerror_exception.py
new file mode 100644
index 0000000..1c3d8b5
--- /dev/null
+++ b/test/input/func_noerror_exception.py
@@ -0,0 +1,7 @@
+""" module doc """
+__revision__ = ''
+
+class MyException(Exception):
+ """a custom exception with its *own* __init__ !!"""
+ def __init__(self, msg):
+ Exception.__init__(self, msg)
diff --git a/test/input/func_noerror_external_classmethod_crash.py b/test/input/func_noerror_external_classmethod_crash.py
new file mode 100644
index 0000000..7674388
--- /dev/null
+++ b/test/input/func_noerror_external_classmethod_crash.py
@@ -0,0 +1,21 @@
+# pylint: disable-msg=W0232,R0903,W0613
+"""tagging a function as a class method cause a crash when checking for
+signature overriding
+"""
+
+def fetch_config(mainattr=None):
+ """return a class method"""
+
+ def fetch_order(cls, attr, var):
+ """a class method"""
+ if attr == mainattr:
+ return var
+ return None
+ fetch_order = classmethod(fetch_order)
+ return fetch_order
+
+class Aaa:
+ """hop"""
+ fetch_order = fetch_config('A')
+
+__revision__ = None
diff --git a/test/input/func_noerror_factory_method.py b/test/input/func_noerror_factory_method.py
new file mode 100644
index 0000000..026ec0f
--- /dev/null
+++ b/test/input/func_noerror_factory_method.py
@@ -0,0 +1,23 @@
+# pylint: disable-msg=R0903
+"""use new astng context sensitive inference"""
+__revision__ = 1
+
+class Super(object):
+ """super class"""
+ def __init__(self):
+ self.bla = None
+
+ def instance(cls):
+ """factory method"""
+ return cls()
+ instance = classmethod(instance)
+
+class Sub(Super):
+ """dub class"""
+ def method(self):
+ """specific method"""
+ print 'method called', self
+
+# should see the Sub.instance() is returning a Sub instance, not a Super
+# instance
+Sub.instance().method()
diff --git a/test/input/func_noerror_indirect_interface.py b/test/input/func_noerror_indirect_interface.py
new file mode 100644
index 0000000..2b240f9
--- /dev/null
+++ b/test/input/func_noerror_indirect_interface.py
@@ -0,0 +1,16 @@
+"""shows a bug where pylint can't find interfaces when they are
+used indirectly. See input/indirect[123].py for details on the
+setup"""
+
+__revision__ = None
+
+from input.indirect2 import AbstractToto
+
+class ConcreteToto(AbstractToto):
+ """abstract to implements an interface requiring machin to be defined"""
+ def __init__(self):
+ self.duh = 2
+
+ def machin(self):
+ """for ifacd"""
+ return self.helper()*2
diff --git a/test/input/func_noerror_inner_classes.py b/test/input/func_noerror_inner_classes.py
new file mode 100644
index 0000000..7d70d19
--- /dev/null
+++ b/test/input/func_noerror_inner_classes.py
@@ -0,0 +1,33 @@
+# pylint: disable-msg=R0903
+"""Backend Base Classes for the schwelm user DB"""
+
+__revision__ = "alpha"
+
+class Aaa(object):
+ """docstring"""
+ def __init__(self):
+ self.__setattr__('a','b')
+
+
+ def one_public(self):
+ """docstring"""
+ pass
+
+ def another_public(self):
+ """docstring"""
+ pass
+
+class Bbb(Aaa):
+ """docstring"""
+ pass
+
+class Ccc(Aaa):
+ """docstring"""
+
+ class Ddd(Aaa):
+ """docstring"""
+ pass
+
+ class Eee(Ddd):
+ """docstring"""
+ pass
diff --git a/test/input/func_noerror_mcs_attr_access.py b/test/input/func_noerror_mcs_attr_access.py
new file mode 100644
index 0000000..c42c061
--- /dev/null
+++ b/test/input/func_noerror_mcs_attr_access.py
@@ -0,0 +1,20 @@
+# pylint: disable-msg=R0903
+"""test attribute access on metaclass"""
+
+
+__revision__ = 'yo'
+
+class Meta(type):
+ """the meta class"""
+ def __init__(mcs, name, bases, dictionary):
+ super(Meta, mcs).__init__(name, bases, dictionary)
+ print mcs, mcs._meta_args
+ delattr(mcs, '_meta_args')
+
+class Test(object):
+ """metaclassed class"""
+ __metaclass__ = Meta
+ _meta_args = ('foo', 'bar')
+
+ def __init__(self):
+ print '__init__', self
diff --git a/test/input/func_noerror_nested_classes.py b/test/input/func_noerror_nested_classes.py
new file mode 100644
index 0000000..96cd366
--- /dev/null
+++ b/test/input/func_noerror_nested_classes.py
@@ -0,0 +1,18 @@
+# pylint: disable-msg=R0903
+"""crash test"""
+
+__revision__ = 1
+
+class Temelekefe:
+ """gloubliboulga"""
+
+ def __init__(self):
+ """nested class with function raise error"""
+ class Toto:
+ """toto nested class"""
+ def __init__(self):
+ self.attr = 2
+ def toto_method(self):
+ """toto nested class method"""
+ print self
+ print 'error ?', self, Toto
diff --git a/test/input/func_noerror_new_style_class.py b/test/input/func_noerror_new_style_class.py
new file mode 100644
index 0000000..8c9ed40
--- /dev/null
+++ b/test/input/func_noerror_new_style_class.py
@@ -0,0 +1,45 @@
+"""check builtin data descriptors such as mode and name attributes
+on a file are correctly handler
+
+bug notified by Pierre Rouleau on 2005-04-24
+"""
+
+__revision__ = None
+
+class File(file):
+ """ Testing new-style class inheritance from file"""
+
+ #
+ def __init__(self, name, mode="r", buffering=-1, verbose=False):
+ """Constructor"""
+
+ self.was_modified = False
+ self.verbose = verbose
+ super(File, self).__init__(name, mode, buffering)
+ if self.verbose:
+ print "File %s is opened. The mode is: %s" % (self.name,
+self.mode)
+
+ #
+ def write(self, a_string):
+ """ Write a string to the file."""
+
+ super(File, self).write(a_string)
+ self.was_modified = True
+
+ #
+ def writelines(self, sequence):
+ """ Write a sequence of strings to the file. """
+
+ super(File, self).writelines(sequence)
+ self.was_modified = True
+
+ #
+ def close(self) :
+ """Close the file."""
+
+ if self.verbose:
+ print "Closing file %s" % self.name
+
+ super(File, self).close()
+ self.was_modified = False
diff --git a/test/input/func_noerror_nonregr.py b/test/input/func_noerror_nonregr.py
new file mode 100644
index 0000000..249e2f0
--- /dev/null
+++ b/test/input/func_noerror_nonregr.py
@@ -0,0 +1,13 @@
+# pylint: disable-msg=W0104
+"""snippets of codes which have at some point made pylint crash"""
+
+__revision__ = 1
+
+def function1(cbarg = lambda: None):
+ """
+ File "/usr/lib/python2.4/site-packages/logilab/astng/scoped_nodes.py", line
+391, in mularg_class
+ i = self.argnames.index(argname)
+ValueError: list.index(x): x not in list
+ """
+ cbarg().x
diff --git a/test/input/func_noerror_object_as_class_attribute.py b/test/input/func_noerror_object_as_class_attribute.py
new file mode 100644
index 0000000..6b442ea
--- /dev/null
+++ b/test/input/func_noerror_object_as_class_attribute.py
@@ -0,0 +1,19 @@
+# pylint: disable-msg=R0903
+"""Test case for the problem described below :
+ - A class extends 'object'
+ - This class defines its own __init__()
+ * pylint will therefore check that baseclasses' init()
+ are called
+ - If this class defines an 'object' attribute, then pylint
+ will use this new definition when trying to retrieve
+ object.__init__()
+"""
+
+__revision__ = None
+
+class Statement(object):
+ """ ... """
+ def __init__(self):
+ pass
+ object = None
+
diff --git a/test/input/func_noerror_overloaded_operator.py b/test/input/func_noerror_overloaded_operator.py
new file mode 100644
index 0000000..eb79141
--- /dev/null
+++ b/test/input/func_noerror_overloaded_operator.py
@@ -0,0 +1,21 @@
+# pylint: disable-msg=C0111,R0903
+"""#3291"""
+__revision__ = 1
+
+class Myarray:
+ def __init__(self, array):
+ self.array = array
+
+ def __mul__(self, val):
+ return Myarray(val)
+
+ def astype(self):
+ return "ASTYPE", self
+
+def randint(maximum):
+ if maximum is not None:
+ return Myarray([1, 2, 3]) * 2
+ else:
+ return int(5)
+
+print randint(1).astype() # we don't wan't an error for astype access
diff --git a/test/input/func_noerror_socket_member.py b/test/input/func_noerror_socket_member.py
new file mode 100644
index 0000000..8c79ff5
--- /dev/null
+++ b/test/input/func_noerror_socket_member.py
@@ -0,0 +1,25 @@
+"""Testing Pylint with the socket module
+
+Pylint Problem
+==============
+
+Version used:
+
+ - Pylint 0.10.0
+ - Logilab common 0.15.0
+ - Logilab astng 0.15.1
+
+False E1101 positive, line 23:
+
+ Instance of '_socketobject' has no 'connect' member
+
+"""
+__revision__ = None
+
+import socket
+
+if __name__ == "__main__":
+
+ SCKT = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ SCKT.connect(('127.0.0.1', 80))
+ SCKT.close()
diff --git a/test/input/func_noerror_static_method.py b/test/input/func_noerror_static_method.py
new file mode 100644
index 0000000..6184961
--- /dev/null
+++ b/test/input/func_noerror_static_method.py
@@ -0,0 +1,29 @@
+"""Checks if static / class methods works fine in Pylint
+"""
+
+__revision__ = ''
+
+class MyClass:
+ """doc
+ """
+ def __init__(self):
+ pass
+
+ def static_met(var1, var2):
+ """This is a static method
+ """
+ print var1, var2
+
+ def class_met(cls, var1):
+ """This is a class method
+ """
+ print cls, var1
+
+ static_met = staticmethod(static_met)
+ class_met = classmethod(class_met)
+
+if __name__ == '__main__':
+ MyClass.static_met("var1","var2")
+ MyClass.class_met("var1")
+
+
diff --git a/test/input/func_noerror_staticmethod_as_decorator.py b/test/input/func_noerror_staticmethod_as_decorator.py
new file mode 100644
index 0000000..e5a44f2
--- /dev/null
+++ b/test/input/func_noerror_staticmethod_as_decorator.py
@@ -0,0 +1,35 @@
+# pylint: disable-msg=R0903
+"""test staticmethod and classmethod as decorator"""
+
+__revision__ = None
+
+class StaticMethod1(object):
+ """staticmethod test"""
+ def __init__(self):
+ pass
+
+ @staticmethod
+ def do_work():
+ "Working..."
+
+ @staticmethod
+ def do_work_with_arg(job):
+ "Working on something"
+ print "Working on %s..." % job
+
+
+class ClassMethod2(object):
+ """classmethod test"""
+ def __init__(self):
+ pass
+
+ @classmethod
+ def do_work(cls):
+ "Working..."
+
+ @classmethod
+ def do_work_with_arg(cls, job):
+ "Working on something"
+ print "Working on %s..." % job
+
+
diff --git a/test/input/func_noerror_w0232.py b/test/input/func_noerror_w0232.py
new file mode 100644
index 0000000..f4c4232
--- /dev/null
+++ b/test/input/func_noerror_w0232.py
@@ -0,0 +1,10 @@
+# pylint: disable-msg=R0903,R0923
+"""check interface and exception without __init__ doesn't print warnings
+"""
+__revision__ = ''
+
+class Interface:
+ """interface without docstring"""
+
+class MyError(Exception):
+ """exception without docstring"""
diff --git a/test/input/func_noerror_yield_return_mix.py b/test/input/func_noerror_yield_return_mix.py
new file mode 100644
index 0000000..bd78633
--- /dev/null
+++ b/test/input/func_noerror_yield_return_mix.py
@@ -0,0 +1,7 @@
+""" module doc """
+__revision__ = None
+
+def somegen():
+ """this kind of mix is OK"""
+ yield 1
+ return
diff --git a/test/input/func_nonascii_noencoding.py b/test/input/func_nonascii_noencoding.py
new file mode 100644
index 0000000..1ba3578
--- /dev/null
+++ b/test/input/func_nonascii_noencoding.py
@@ -0,0 +1,5 @@
+"""test file with non ascii characters and no encoding declaration"""
+
+__revision__ = ''
+
+YOP = 'héhéhé'
diff --git a/test/input/func_r0901.py b/test/input/func_r0901.py
new file mode 100644
index 0000000..d30d270
--- /dev/null
+++ b/test/input/func_r0901.py
@@ -0,0 +1,27 @@
+# pylint: disable-msg=W0232, R0903
+"""test max parents"""
+__revision__ = None
+
+class Aaaa:
+ """yo"""
+class Bbbb:
+ """yo"""
+class Cccc:
+ """yo"""
+class Dddd:
+ """yo"""
+class Eeee:
+ """yo"""
+class Ffff:
+ """yo"""
+class Gggg:
+ """yo"""
+class Hhhh:
+ """yo"""
+
+class Iiii(Aaaa, Bbbb, Cccc, Dddd, Eeee, Ffff, Gggg, Hhhh):
+ """yo"""
+
+class Jjjj(Iiii):
+ """yo"""
+
diff --git a/test/input/func_r0902.py b/test/input/func_r0902.py
new file mode 100644
index 0000000..59d4100
--- /dev/null
+++ b/test/input/func_r0902.py
@@ -0,0 +1,28 @@
+# pylint: disable-msg=R0903
+"""test max instance attributes"""
+__revision__ = None
+
+class Aaaa:
+ """yo"""
+ def __init__(self):
+ self.aaaa = 1
+ self.bbbb = 2
+ self.cccc = 3
+ self.dddd = 4
+ self.eeee = 5
+ self.ffff = 6
+ self.gggg = 7
+ self.hhhh = 8
+ self.iiii = 9
+ self.jjjj = 10
+ self._aaaa = 1
+ self._bbbb = 2
+ self._cccc = 3
+ self._dddd = 4
+ self._eeee = 5
+ self._ffff = 6
+ self._gggg = 7
+ self._hhhh = 8
+ self._iiii = 9
+ self._jjjj = 10
+ self.tomuch = None
diff --git a/test/input/func_r0903.py b/test/input/func_r0903.py
new file mode 100644
index 0000000..4c11929
--- /dev/null
+++ b/test/input/func_r0903.py
@@ -0,0 +1,13 @@
+"""test min methods"""
+__revision__ = None
+
+class Aaaa:
+ """yo"""
+ def __init__(self):
+ pass
+ def meth1(self):
+ """hehehe"""
+ print self
+ def _dontcount(self):
+ """not public"""
+ print self
diff --git a/test/input/func_r0904.py b/test/input/func_r0904.py
new file mode 100644
index 0000000..abb4d06
--- /dev/null
+++ b/test/input/func_r0904.py
@@ -0,0 +1,73 @@
+# pylint: disable-msg=R0201
+"""test max methods"""
+__revision__ = None
+class Aaaa:
+ """yo"""
+ def __init__(self):
+ pass
+
+ def meth1(self):
+ """hehehe"""
+
+ def meth2(self):
+ """hehehe"""
+
+ def meth3(self):
+ """hehehe"""
+
+ def meth4(self):
+ """hehehe"""
+
+ def meth5(self):
+ """hehehe"""
+
+ def meth6(self):
+ """hehehe"""
+
+ def meth7(self):
+ """hehehe"""
+
+ def meth8(self):
+ """hehehe"""
+
+ def meth9(self):
+ """hehehe"""
+
+ def meth10(self):
+ """hehehe"""
+
+ def meth11(self):
+ """hehehe"""
+
+ def meth12(self):
+ """hehehe"""
+
+ def meth13(self):
+ """hehehe"""
+
+ def meth14(self):
+ """hehehe"""
+
+ def meth15(self):
+ """hehehe"""
+
+ def meth16(self):
+ """hehehe"""
+
+ def meth17(self):
+ """hehehe"""
+
+ def meth18(self):
+ """hehehe"""
+
+ def meth19(self):
+ """hehehe"""
+
+ def meth20(self):
+ """hehehe"""
+
+ def meth21(self):
+ """hehehe"""
+
+ def _dontcount(self):
+ """not public"""
diff --git a/test/input/func_r0921.py b/test/input/func_r0921.py
new file mode 100644
index 0000000..b9f2de2
--- /dev/null
+++ b/test/input/func_r0921.py
@@ -0,0 +1,15 @@
+"""test max methods"""
+__revision__ = None
+
+class Aaaa:
+ """yo"""
+ def __init__(self):
+ pass
+
+ def meth1(self):
+ """hehehe"""
+ raise NotImplementedError
+
+ def meth2(self):
+ """hehehe"""
+ return 'Yo', self
diff --git a/test/input/func_r0922.py b/test/input/func_r0922.py
new file mode 100644
index 0000000..da7dfcf
--- /dev/null
+++ b/test/input/func_r0922.py
@@ -0,0 +1,21 @@
+"""test max methods"""
+__revision__ = None
+
+class Aaaa:
+ """yo"""
+ def __init__(self):
+ pass
+
+ def meth1(self):
+ """hehehe"""
+ raise NotImplementedError
+
+ def meth2(self):
+ """hehehe"""
+ return 'Yo', self
+
+class Bbbb(Aaaa):
+ """yeah"""
+ def meth1(self):
+ """hehehe bis"""
+ return "yeah", self
diff --git a/test/input/func_r0923.py b/test/input/func_r0923.py
new file mode 100644
index 0000000..8dacf1e
--- /dev/null
+++ b/test/input/func_r0923.py
@@ -0,0 +1,32 @@
+"""test max methods"""
+__revision__ = None
+
+from logilab.common.interface import Interface
+
+class IAaaa(Interface):
+ """yo"""
+
+ def meth1(self):
+ """hehehe"""
+
+class IBbbb(Interface):
+ """yo"""
+
+ def meth1(self):
+ """hehehe"""
+
+class Concret:
+ """implements IBbbb"""
+ __implements__ = IBbbb
+
+ def __init__(self):
+ pass
+
+ def meth1(self):
+ """hehehe"""
+ return "et hop", self
+
+ def meth2(self):
+ """hehehe"""
+ return "et hop", self
+
diff --git a/test/input/func_reqattrs.py b/test/input/func_reqattrs.py
new file mode 100644
index 0000000..fb1e2b6
--- /dev/null
+++ b/test/input/func_reqattrs.py
@@ -0,0 +1 @@
+"""docstring"""
diff --git a/test/input/func_return_outside_func.py b/test/input/func_return_outside_func.py
new file mode 100644
index 0000000..440798d
--- /dev/null
+++ b/test/input/func_return_outside_func.py
@@ -0,0 +1,3 @@
+"""This is gramatically correct, but it's still a SyntaxError"""
+__revision__ = None
+return
diff --git a/test/input/func_return_yield_mix.py b/test/input/func_return_yield_mix.py
new file mode 100644
index 0000000..93b8051
--- /dev/null
+++ b/test/input/func_return_yield_mix.py
@@ -0,0 +1,8 @@
+"""pylint should detect yield and return mix inside genrators"""
+__revision__ = None
+def somegen():
+ """this is a bad generator"""
+ if True:
+ return 1
+ else:
+ yield 2
diff --git a/test/input/func_return_yield_mix2.py b/test/input/func_return_yield_mix2.py
new file mode 100644
index 0000000..ad85c62
--- /dev/null
+++ b/test/input/func_return_yield_mix2.py
@@ -0,0 +1,8 @@
+"""pylint should detect yield and return mix inside genrators"""
+__revision__ = None
+def somegen():
+ """this is a bad generator"""
+ if True:
+ yield 1
+ else:
+ return 2
diff --git a/test/input/func_scope_regrtest.py b/test/input/func_scope_regrtest.py
new file mode 100644
index 0000000..98dd8be
--- /dev/null
+++ b/test/input/func_scope_regrtest.py
@@ -0,0 +1,15 @@
+# pylint: disable-msg=R0903,W0232
+"""check for scope problems"""
+
+__revision__ = None
+
+class Well(object):
+ """well"""
+ class Data:
+ """base hidden class"""
+ class Sub(Data):
+ """whaou, is Data found???"""
+ yo = Data()
+ def func(self):
+ """check Sub is not defined here"""
+ return Sub(), self
diff --git a/test/input/func_syntax_error.py b/test/input/func_syntax_error.py
new file mode 100644
index 0000000..43fa087
--- /dev/null
+++ b/test/input/func_syntax_error.py
@@ -0,0 +1 @@
+def toto
diff --git a/test/input/func_toolonglines.py b/test/input/func_toolonglines.py
new file mode 100644
index 0000000..76a4018
--- /dev/null
+++ b/test/input/func_toolonglines.py
@@ -0,0 +1,4 @@
+##########################################################################################
+""" that one is too long tooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo"""
+
+__revision__ = ''
diff --git a/test/input/func_typecheck_callfunc_assigment.py b/test/input/func_typecheck_callfunc_assigment.py
new file mode 100644
index 0000000..82130e3
--- /dev/null
+++ b/test/input/func_typecheck_callfunc_assigment.py
@@ -0,0 +1,56 @@
+# pylint: disable-msg=R0921
+"""check assigment to function call where the function doesn't return
+
+ 'E1111': ('Assigning to function call which doesn\'t return',
+ 'Used when an assigment is done on a function call but the \
+ infered function doesn\'t return anything.'),
+ 'W1111': ('Assigning to function call which only returns None',
+ 'Used when an assigment is done on a function call but the \
+ infered function returns nothing but None.'),
+
+"""
+from __future__ import generators
+__revision__ = None
+
+
+def func_no_return():
+ """function without return"""
+ print 'dougloup'
+
+A = func_no_return()
+
+
+def func_return_none():
+ """function returning none"""
+ print 'dougloup'
+ return None
+
+A = func_return_none()
+
+
+def func_return_none_and_smth():
+ """function returning none and something else"""
+ print 'dougloup'
+ if __revision__:
+ return None
+ return 3
+
+A = func_return_none_and_smth()
+
+def generator():
+ """no problemo"""
+ yield __revision__
+
+A = generator()
+
+class Abstract(object):
+ """bla bla"""
+
+ def abstract_method(self):
+ """use to return something in concrete implementation"""
+ raise NotImplementedError
+
+ def use_abstract(self):
+ """should not issue E1111"""
+ var = self.abstract_method()
+ print var
diff --git a/test/input/func_typecheck_getattr.py b/test/input/func_typecheck_getattr.py
new file mode 100644
index 0000000..1b628fd
--- /dev/null
+++ b/test/input/func_typecheck_getattr.py
@@ -0,0 +1,66 @@
+# pylint: disable-msg=
+"""check getattr if inference succeed"""
+
+__revision__ = None
+
+class Provider:
+ """provide some attributes and method"""
+ cattr = 4
+ def __init__(self):
+ self.attr = 4
+ def method(self, val):
+ """impressive method"""
+ return self.attr * val
+ def hophop(self):
+ """hop method"""
+ print 'hop hop hop', self
+
+
+class Client:
+ """use provider class"""
+
+ def __init__(self):
+ self._prov = Provider()
+ self._prov_attr = Provider.cattr
+ self._prov_attr2 = Provider.cattribute
+ self.set_later = 0
+
+ def set_set_later(self, value):
+ """set set_later attribute (introduce an inference ambiguity)"""
+ self.set_later = value
+
+ def use_method(self):
+ """use provider's method"""
+ self._prov.hophop()
+ self._prov.hophophop()
+
+ def use_attr(self):
+ """use provider's attr"""
+ print self._prov.attr
+ print self._prov.attribute
+
+ def debug(self):
+ """print debug information"""
+ print self.__class__.__name__
+ print self.__doc__
+ print self.__dict__
+ print self.__module__
+
+ def test_bt_types(self):
+ """test access to unexistant member of builtin types"""
+ lis = []
+ lis.apppend(self)
+ dic = {}
+ dic.set(self)
+ tup = ()
+ tup.append(self)
+ string = 'toto'
+ print string.loower()
+ uni = u'toto'
+ print uni.loower()
+ integer = 1
+ print integer.whatever
+
+print object.__init__
+print property.__init__
+print Client().set_later.lower()
diff --git a/test/input/func_typecheck_non_callable_call.py b/test/input/func_typecheck_non_callable_call.py
new file mode 100644
index 0000000..75b1224
--- /dev/null
+++ b/test/input/func_typecheck_non_callable_call.py
@@ -0,0 +1,37 @@
+# pylint: disable-msg=R0903
+"""
+ 'E1102': ('%s is not callable',
+ 'Used when an object being called has been infered to a non \
+ callable object'),
+"""
+
+__revision__ = None
+
+__revision__()
+
+def correct():
+ """callable object"""
+ return 1
+
+__revision__ = correct()
+
+class Correct(object):
+ """callable object"""
+
+class MetaCorrect(object):
+ """callable object"""
+ def __call__(self):
+ return self
+
+INSTANCE = Correct()
+CALLABLE_INSTANCE = MetaCorrect()
+CORRECT = CALLABLE_INSTANCE()
+INCORRECT = INSTANCE()
+LIST = []
+INCORRECT = LIST()
+DICT = {}
+INCORRECT = DICT()
+TUPLE = ()
+INCORRECT = TUPLE()
+INT = 1
+INCORRECT = INT()
diff --git a/test/input/func_undefined_var.py b/test/input/func_undefined_var.py
new file mode 100644
index 0000000..3166260
--- /dev/null
+++ b/test/input/func_undefined_var.py
@@ -0,0 +1,26 @@
+"""test access to undefined variables"""
+
+__revision__ = '$Id:'
+
+DEFINED = 1
+
+if DEFINED != 1:
+ if DEFINED in (unknown, DEFINED):
+ DEFINED += 1
+
+
+def in_method(var):
+ """method doc"""
+ var = nomoreknown
+ assert var
+
+DEFINED = {DEFINED:__revision__}
+DEFINED[__revision__] = OTHER = 'move this is astng test'
+
+OTHER += '$'
+
+def bad_default(var, default=unknown2):
+ """function with defaut arg's value set to an unexistant name"""
+ print var, default
+ print xxxx
+ print xxxx #see story #1000
diff --git a/test/input/func_unknown_encoding.py b/test/input/func_unknown_encoding.py
new file mode 100644
index 0000000..31deabd
--- /dev/null
+++ b/test/input/func_unknown_encoding.py
@@ -0,0 +1,6 @@
+# -*- coding: IBO-8859-1 -*-
+""" check correct unknown encoding declaration
+"""
+
+__revision__ = 'éééé'
+
diff --git a/test/input/func_unreachable.py b/test/input/func_unreachable.py
new file mode 100644
index 0000000..1cabce7
--- /dev/null
+++ b/test/input/func_unreachable.py
@@ -0,0 +1,22 @@
+"""docstring"""
+
+__revision__ = ''
+
+def func1():
+ """docstring"""
+ return 1
+ print 'unreachable'
+
+def func2():
+ """docstring"""
+ while 1:
+ break
+ print 'unreachable'
+
+def func3():
+ """docstring"""
+ for i in (1, 2, 3):
+ print i
+ continue
+ print 'unreachable'
+
diff --git a/test/input/func_use_for_or_listcomp_var.py b/test/input/func_use_for_or_listcomp_var.py
new file mode 100644
index 0000000..b0db336
--- /dev/null
+++ b/test/input/func_use_for_or_listcomp_var.py
@@ -0,0 +1,21 @@
+"""test a warning is triggered when using for a lists comprehension variable"""
+
+__revision__ = 'yo'
+
+TEST_LC = [C for C in __revision__ if C.isalpha()]
+print C # WARN
+C = 4
+print C # this one shouldn't trigger any warning
+
+B = [B for B in __revision__ if B.isalpha()]
+print B # nor this one
+
+for var1, var2 in TEST_LC:
+ var1 = var2 + 4
+print var1 # WARN
+
+for note in __revision__:
+ note.something()
+for line in __revision__:
+ for note in line:
+ A = note.anotherthing()
diff --git a/test/input/func_variables_unused_name_from_wilcard_import.py b/test/input/func_variables_unused_name_from_wilcard_import.py
new file mode 100644
index 0000000..c59b731
--- /dev/null
+++ b/test/input/func_variables_unused_name_from_wilcard_import.py
@@ -0,0 +1,3 @@
+"""check unused import from a wildcard import"""
+from input.func_w0611 import *
+__revision__ = None
diff --git a/test/input/func_w0101.py b/test/input/func_w0101.py
new file mode 100644
index 0000000..fe543aa
--- /dev/null
+++ b/test/input/func_w0101.py
@@ -0,0 +1,28 @@
+"""test max returns
+"""
+
+__revision__ = ''
+
+def stupid_function(arg):
+ """reallly stupid function"""
+ if arg == 1:
+ return 1
+ elif arg == 2:
+ return 2
+ elif arg == 3:
+ return 3
+ elif arg == 4:
+ return 4
+ elif arg == 5:
+ return 5
+ elif arg == 6:
+ return 6
+ elif arg == 7:
+ return 7
+ elif arg == 8:
+ return 8
+ elif arg == 9:
+ return 9
+ elif arg == 10:
+ return 10
+ return None
diff --git a/test/input/func_w0102.py b/test/input/func_w0102.py
new file mode 100644
index 0000000..b096fff
--- /dev/null
+++ b/test/input/func_w0102.py
@@ -0,0 +1,53 @@
+# pylint: disable-msg=R0201
+"""docstring"""
+__revision__ = ''
+
+class AAAA:
+ """docstring"""
+ def __init__(self):
+ pass
+ def method1(self):
+ """docstring"""
+
+ def method2(self):
+ """docstring"""
+
+ def method2(self):
+ """docstring"""
+
+class AAAA:
+ """docstring"""
+ def __init__(self):
+ pass
+ def yeah(self):
+ """hehehe"""
+ def yoo(self):
+ """yoo"""
+def func1():
+ """docstring"""
+
+def func2():
+ """docstring"""
+
+def func2():
+ """docstring"""
+ __revision__ = 1
+ return __revision__
+
+if __revision__:
+ def exclusive_func():
+ "docstring"
+else:
+ def exclusive_func():
+ "docstring"
+
+try:
+ def exclusive_func2():
+ "docstring"
+except TypeError:
+ def exclusive_func2():
+ "docstring"
+else:
+ def exclusive_func2():
+ "this one redefine the one defined line 42"
+
diff --git a/test/input/func_w0103.py b/test/input/func_w0103.py
new file mode 100644
index 0000000..9417cb5
--- /dev/null
+++ b/test/input/func_w0103.py
@@ -0,0 +1,8 @@
+"""test max branch
+"""
+
+__revision__ = ''
+
+def stupid_function(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9):
+ """reallly stupid function"""
+ print arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9
diff --git a/test/input/func_w0104.py b/test/input/func_w0104.py
new file mode 100644
index 0000000..bddf001
--- /dev/null
+++ b/test/input/func_w0104.py
@@ -0,0 +1,12 @@
+"""test max branch
+"""
+
+__revision__ = ''
+
+def stupid_function(arg1, arg2, arg3, arg4, arg5):
+ """reallly stupid function"""
+ arg6, arg7, arg8, arg9 = arg1, arg2, arg3, arg4
+ print arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9
+ loc1, loc2, loc3, loc4, loc5, loc6, loc7 = arg1, arg2, arg3, arg4, arg5, \
+ arg6, arg7
+ print loc1, loc2, loc3, loc4, loc5, loc6, loc7
diff --git a/test/input/func_w0105.py b/test/input/func_w0105.py
new file mode 100644
index 0000000..430cfd4
--- /dev/null
+++ b/test/input/func_w0105.py
@@ -0,0 +1,62 @@
+"""test max branch
+"""
+
+__revision__ = ''
+
+def stupid_function(arg):
+ """reallly stupid function"""
+ if arg == 1:
+ print 1
+ elif arg == 2:
+ print 2
+ elif arg == 3:
+ print 3
+ elif arg == 4:
+ print 4
+ elif arg == 5:
+ print 5
+ elif arg == 6:
+ print 6
+ elif arg == 7:
+ print 7
+ elif arg == 8:
+ print 8
+ elif arg == 9:
+ print 9
+ elif arg == 10:
+ print 10
+ elif arg < 1:
+ print 0
+ print 100
+ arg = 0
+ for val in range(arg):
+ print val
+ print val
+ print val
+ print val
+ print val
+ print val
+ print val
+ print val
+ print val
+ print val
+ print val
+ print val
+ print val
+ print val
+ print val
+ print val
+ print val
+ print val
+ print val
+ print val
+ print val
+ print val
+ print val
+ print val
+ print val
+ print val
+ print val
+ print val
+ print val
+ print val
diff --git a/test/input/func_w0109.py b/test/input/func_w0109.py
new file mode 100644
index 0000000..ba7a679
--- /dev/null
+++ b/test/input/func_w0109.py
@@ -0,0 +1,7 @@
+"""test empty docstrings
+"""
+
+__revision__ = ''
+
+def function():
+ """"""
diff --git a/test/input/func_w0110.py b/test/input/func_w0110.py
new file mode 100644
index 0000000..c9d9311
--- /dev/null
+++ b/test/input/func_w0110.py
@@ -0,0 +1,10 @@
+"""test too short name
+"""
+
+__revision__ = 1
+
+A = None
+
+def a():
+ """yo"""
+ pass
diff --git a/test/input/func_w0111.py b/test/input/func_w0111.py
new file mode 100644
index 0000000..f8ad440
--- /dev/null
+++ b/test/input/func_w0111.py
@@ -0,0 +1,10 @@
+"""test black listed name
+"""
+
+__revision__ = 1
+
+A = None
+
+def baz():
+ """yo"""
+ pass
diff --git a/test/input/func_w0112.py b/test/input/func_w0112.py
new file mode 100644
index 0000000..5cd01ae
--- /dev/null
+++ b/test/input/func_w0112.py
@@ -0,0 +1,37 @@
+"""test max branch
+"""
+
+__revision__ = ''
+
+def stupid_function(arg):
+ """reallly stupid function"""
+ if arg == 1:
+ print 1
+ elif arg == 2:
+ print 2
+ elif arg == 3:
+ print 3
+ elif arg == 4:
+ print 4
+ elif arg == 5:
+ print 5
+ elif arg == 6:
+ print 6
+ elif arg == 7:
+ print 7
+ elif arg == 8:
+ print 8
+ elif arg == 9:
+ print 9
+ elif arg == 10:
+ print 10
+ else:
+ if arg < 1:
+ print 0
+ else:
+ print 100
+ arg = 0
+ if arg:
+ print None
+ else:
+ print arg
diff --git a/test/input/func_w0122.py b/test/input/func_w0122.py
new file mode 100644
index 0000000..178c252
--- /dev/null
+++ b/test/input/func_w0122.py
@@ -0,0 +1,13 @@
+"""test global statement"""
+
+__revision__ = 0
+
+exec 'a = __revision__'
+exec 'a = 1' in {}
+
+exec 'a = 1' in globals()
+
+def func():
+ """exec in local scope"""
+ exec 'b = 1'
+
diff --git a/test/input/func_w0133.py b/test/input/func_w0133.py
new file mode 100644
index 0000000..c0304ec
--- /dev/null
+++ b/test/input/func_w0133.py
@@ -0,0 +1,54 @@
+# pylint: disable-msg=R0903,R0201
+"""test Invalid name"""
+
+__revision__ = 1
+
+def Run():
+ """method without any good name"""
+ class B:
+ """nested class should not be tested has a variable"""
+ def __init__(self):
+ pass
+ bBb = 1
+ return A, bBb
+
+def run():
+ """anothrer method without only good name"""
+ class Aaa:
+ """nested class should not be tested has a variable"""
+ def __init__(self):
+ pass
+ bbb = 1
+ return Aaa(bbb)
+
+A = None
+
+def HOHOHOHO():
+ """yo"""
+ HIHIHI = 1
+ print HIHIHI
+
+class xyz:
+ """yo"""
+ def __init__(self):
+ pass
+
+ def Youplapoum(self):
+ """bad method name"""
+
+
+def nested_args(arg1, (arg21, arg22)):
+ """function with nested arguments"""
+ print arg1, arg21, arg22
+
+
+GOOD_CONST_NAME = ''
+benpasceluila = 0
+
+class Correct:
+ """yo"""
+ def __init__(self):
+ self.cava = 12
+ self._Ca_va_Pas = None
+
+V = [WHAT_Ever_inListComp for WHAT_Ever_inListComp in GOOD_CONST_NAME]
diff --git a/test/input/func_w0151.py b/test/input/func_w0151.py
new file mode 100644
index 0000000..4c55446
--- /dev/null
+++ b/test/input/func_w0151.py
@@ -0,0 +1,7 @@
+"""check black listed builtins
+"""
+
+__revision__ = apply(map, (str, (1, 2, 3)))
+
+YYYYY = map(str, (1, 2, 3))
+
diff --git a/test/input/func_w0152.py b/test/input/func_w0152.py
new file mode 100644
index 0000000..5054d90
--- /dev/null
+++ b/test/input/func_w0152.py
@@ -0,0 +1,14 @@
+"""check use of * or **
+"""
+
+from operator import add
+__revision__ = reduce(*(add, (1, 2, 3)))
+
+
+def func(arg1, arg2):
+ """magic function
+ """
+ return arg2, arg1
+
+MYDICT = {'arg1':2, 'arg2': 4}
+func(**MYDICT)
diff --git a/test/input/func_w0202.py b/test/input/func_w0202.py
new file mode 100644
index 0000000..4d36d40
--- /dev/null
+++ b/test/input/func_w0202.py
@@ -0,0 +1,17 @@
+"""check static method with self or cls as first argument"""
+
+__revision__ = None
+
+class Abcd:
+ """dummy"""
+
+ def method1(self):
+ """hehe"""
+ method1 = staticmethod(method1)
+
+ def method2(cls):
+ """hehe"""
+ method2 = staticmethod(method2)
+
+ def __init__(self):
+ pass
diff --git a/test/input/func_w0205.py b/test/input/func_w0205.py
new file mode 100644
index 0000000..d2e3b29
--- /dev/null
+++ b/test/input/func_w0205.py
@@ -0,0 +1,24 @@
+"""check different signatures"""
+
+__revision__ = 0
+
+class Abcd:
+ '''dummy'''
+ def __init__(self):
+ self.aarg = False
+ def abcd(self, aaa=1, bbbb=None):
+ """hehehe"""
+ print self, aaa, bbbb
+ def args(self):
+ """gaarrrggll"""
+ self.aarg = True
+
+class Cdef(Abcd):
+ """dummy"""
+ def __init__(self, aaa):
+ Abcd.__init__(self)
+ self.aaa = aaa
+
+ def abcd(self, aaa, bbbb=None):
+ """hehehe"""
+ print self, aaa, bbbb
diff --git a/test/input/func_w0223.py b/test/input/func_w0223.py
new file mode 100644
index 0000000..e1e81bf
--- /dev/null
+++ b/test/input/func_w0223.py
@@ -0,0 +1,27 @@
+# pylint: disable-msg=R0903,R0922
+"""test overriding of abstract method
+"""
+
+__revision__ = '$Id: func_w0223.py,v 1.2 2004-09-29 08:35:13 syt Exp $'
+
+class Abstract:
+ """abstract class
+ """
+ def aaaa(self):
+ """should be overridden in concrete class"""
+ raise NotImplementedError()
+
+
+ def bbbb(self):
+ """should be overridden in concrete class"""
+ raise NotImplementedError()
+
+ def __init__(self):
+ pass
+
+class Concret(Abstract):
+ """concret class"""
+
+ def aaaa(self):
+ """overidden form Abstract"""
+ print self
diff --git a/test/input/func_w0231.py b/test/input/func_w0231.py
new file mode 100644
index 0000000..3f592ec
--- /dev/null
+++ b/test/input/func_w0231.py
@@ -0,0 +1,38 @@
+# pylint: disable-msg=R0903
+"""test for __init__ not called
+"""
+
+__revision__ = '$Id: func_w0231.py,v 1.3 2004-09-29 08:35:13 syt Exp $'
+
+class AAAA:
+ """ancestor 1"""
+
+ def __init__(self):
+ print 'init', self
+
+class BBBB:
+ """ancestor 2"""
+
+ def __init__(self):
+ print 'init', self
+
+class CCCC:
+ """ancestor 3"""
+
+
+class ZZZZ(AAAA, BBBB, CCCC):
+ """derived class"""
+
+ def __init__(self):
+ AAAA.__init__(self)
+
+class NewStyleA(object):
+ """new style class"""
+ def __init__(self):
+ super(NewStyleA, self).__init__()
+ print 'init', self
+
+class NewStyleB(NewStyleA):
+ """derived new style class"""
+ def __init__(self):
+ super(NewStyleB, self).__init__()
diff --git a/test/input/func_w0233.py b/test/input/func_w0233.py
new file mode 100644
index 0000000..a667010
--- /dev/null
+++ b/test/input/func_w0233.py
@@ -0,0 +1,28 @@
+# pylint: disable-msg=R0903,W0212,W0403,W0406
+"""test for call to __init__ from a non ancestor class
+"""
+
+__revision__ = '$Id: func_w0233.py,v 1.2 2004-09-29 08:35:13 syt Exp $'
+
+class AAAA:
+ """ancestor 1"""
+
+ def __init__(self):
+ print 'init', self
+ BBBBMixin.__init__(self)
+
+class BBBBMixin:
+ """ancestor 2"""
+
+ def __init__(self):
+ print 'init', self
+
+import nonexistant
+import func_w0233
+class CCC(BBBBMixin, func_w0233.AAAA, func_w0233.BBBB, nonexistant.AClass):
+ """mix different things, some inferable some not"""
+ def __init__(self):
+ BBBBMixin.__init__(self)
+ func_w0233.AAAA.__init__(self)
+ func_w0233.BBBB.__init__(self)
+ nonexistant.AClass.__init__(self)
diff --git a/test/input/func_w0302.py b/test/input/func_w0302.py
new file mode 100644
index 0000000..a78f479
--- /dev/null
+++ b/test/input/func_w0302.py
@@ -0,0 +1,1016 @@
+"""test too much line in modules
+"""
+
+__revision__ = 0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ZERFZAER = 3
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+HEHEHE = 2
diff --git a/test/input/func_w0312.py b/test/input/func_w0312.py
new file mode 100644
index 0000000..06a8110
--- /dev/null
+++ b/test/input/func_w0312.py
@@ -0,0 +1,11 @@
+"""test mixed tabs and spaces"""
+
+__revision__ = 1
+
+def spaces_func():
+ """yo"""
+ print "yo"
+
+def tab_func():
+ """yo"""
+ print "yo"
diff --git a/test/input/func_w0331.py b/test/input/func_w0331.py
new file mode 100644
index 0000000..7e34523
--- /dev/null
+++ b/test/input/func_w0331.py
@@ -0,0 +1,7 @@
+"""check use of <>
+"""
+
+__revision__ = 1
+
+if __revision__ <> 2:
+ __revision__ = 2
diff --git a/test/input/func_w0332.py b/test/input/func_w0332.py
new file mode 100644
index 0000000..ff078ef
--- /dev/null
+++ b/test/input/func_w0332.py
@@ -0,0 +1,4 @@
+"""check use of l as long int marker
+"""
+
+__revision__ = 1l
diff --git a/test/input/func_w0401.py b/test/input/func_w0401.py
new file mode 100644
index 0000000..6b7a64b
--- /dev/null
+++ b/test/input/func_w0401.py
@@ -0,0 +1,9 @@
+"""test cyclic import
+"""
+__revision__ = 0
+
+
+from input import w0401_cycle
+
+if __revision__:
+ print w0401_cycle
diff --git a/test/input/func_w0402.py b/test/input/func_w0402.py
new file mode 100644
index 0000000..53752db
--- /dev/null
+++ b/test/input/func_w0402.py
@@ -0,0 +1,9 @@
+"""test wildard import
+"""
+__revision__ = 0
+
+from input.func_fixme import *
+
+def abcd():
+ """use imports"""
+ function()
diff --git a/test/input/func_w0403.py b/test/input/func_w0403.py
new file mode 100644
index 0000000..72fb795
--- /dev/null
+++ b/test/input/func_w0403.py
@@ -0,0 +1,12 @@
+"""test deprecated module
+"""
+
+__revision__ = 0
+
+
+if __revision__:
+ import Bastion
+ print Bastion
+ # false positive (#10061)
+ import stringfile
+ print stringfile
diff --git a/test/input/func_w0404.py b/test/input/func_w0404.py
new file mode 100644
index 0000000..fd38e6a
--- /dev/null
+++ b/test/input/func_w0404.py
@@ -0,0 +1,10 @@
+"""test relative import
+"""
+
+__revision__ = 0
+
+import func_w0302
+
+if __revision__:
+ print func_w0302
+
diff --git a/test/input/func_w0405.py b/test/input/func_w0405.py
new file mode 100644
index 0000000..745c615
--- /dev/null
+++ b/test/input/func_w0405.py
@@ -0,0 +1,31 @@
+"""check reimport
+"""
+
+__revision__ = 0
+
+import os
+from os.path import join, exists
+
+import os
+import re as _re
+
+_re.match('yo', '.*')
+
+if __revision__:
+ print os
+ from os.path import exists
+ print join, exists
+
+def func(yooo):
+ """reimport in different scope"""
+ import os as ass
+ ass.remove(yooo)
+ import re
+ re.compile('.*')
+
+if 1:
+ import sys
+ print sys.modules
+else:
+ print 'bla'
+ import sys
diff --git a/test/input/func_w0406.py b/test/input/func_w0406.py
new file mode 100644
index 0000000..e20508f
--- /dev/null
+++ b/test/input/func_w0406.py
@@ -0,0 +1,9 @@
+"""test module importing itself
+"""
+__revision__ = 0
+
+import func_w0406
+
+if __revision__:
+ print func_w0406
+
diff --git a/test/input/func_w0611.py b/test/input/func_w0611.py
new file mode 100644
index 0000000..2c37966
--- /dev/null
+++ b/test/input/func_w0611.py
@@ -0,0 +1,22 @@
+"""check unused import
+"""
+__revision__ = 1
+import os
+import sys
+
+class NonRegr:
+ """???"""
+ def __init__(self):
+ print 'initialized'
+
+ def sys(self):
+ """should not get sys from there..."""
+ print self, sys
+
+ def dummy(self, truc):
+ """yo"""
+ return self, truc
+
+ def blop(self):
+ """yo"""
+ print self, 'blip'
diff --git a/test/input/func_w0612.py b/test/input/func_w0612.py
new file mode 100644
index 0000000..43b551c
--- /dev/null
+++ b/test/input/func_w0612.py
@@ -0,0 +1,8 @@
+"""test unused variable
+"""
+
+__revision__ = 0
+
+def function():
+ """"yo"""
+ aaaa = 1
diff --git a/test/input/func_w0613.py b/test/input/func_w0613.py
new file mode 100644
index 0000000..0725335
--- /dev/null
+++ b/test/input/func_w0613.py
@@ -0,0 +1,18 @@
+# pylint: disable-msg=R0903
+"""test unused argument
+"""
+
+__revision__ = 1
+
+def function(arg=1):
+ """ignore arg"""
+
+
+class AAAA:
+ """dummy class"""
+
+ def method(self, arg):
+ """dummy method"""
+ print self
+ def __init__(self):
+ pass
diff --git a/test/input/func_w0622.py b/test/input/func_w0622.py
new file mode 100644
index 0000000..ea9d1b2
--- /dev/null
+++ b/test/input/func_w0622.py
@@ -0,0 +1,11 @@
+# pylint: disable-msg=C0103
+"""test built-in redefinition
+"""
+__revision__ = 0
+
+def function():
+ """yo"""
+ type = 1
+ print type
+
+map = {}
diff --git a/test/input/func_w0701.py b/test/input/func_w0701.py
new file mode 100644
index 0000000..9c1b727
--- /dev/null
+++ b/test/input/func_w0701.py
@@ -0,0 +1,12 @@
+"""test string exception
+"""
+
+__revision__ = ''
+
+def function1():
+ """hehehe"""
+ raise "String Exception"
+
+def function2():
+ """hehehe"""
+ raise 'exception', 'message'
diff --git a/test/input/func_w0702.py b/test/input/func_w0702.py
new file mode 100644
index 0000000..38a6417
--- /dev/null
+++ b/test/input/func_w0702.py
@@ -0,0 +1,17 @@
+"""check empty except statement
+"""
+
+__revision__ = 0
+
+if __revision__:
+ try:
+ print __revision__
+ except:
+ print 'error'
+
+try:
+ __revision__ += 1
+except TypeError:
+ print 'error'
+except Exception:
+ print 'error'
diff --git a/test/input/func_w0703.py b/test/input/func_w0703.py
new file mode 100644
index 0000000..4040540
--- /dev/null
+++ b/test/input/func_w0703.py
@@ -0,0 +1,9 @@
+"""check empty except statement
+"""
+
+__revision__ = 0
+
+try:
+ __revision__ += 1
+except Exception:
+ print 'error'
diff --git a/test/input/func_w0704.py b/test/input/func_w0704.py
new file mode 100644
index 0000000..e8c6432
--- /dev/null
+++ b/test/input/func_w0704.py
@@ -0,0 +1,16 @@
+"""test empty except
+"""
+
+__revision__ = 1
+
+try:
+ __revision__ += 1
+except TypeError:
+ pass
+
+try:
+ __revision__ += 1
+except TypeError:
+ pass
+else:
+ __revision__ = None
diff --git a/test/input/func_w0705.py b/test/input/func_w0705.py
new file mode 100644
index 0000000..898e2b8
--- /dev/null
+++ b/test/input/func_w0705.py
@@ -0,0 +1,46 @@
+"""test misordered except
+"""
+
+__revision__ = 1
+
+try:
+ __revision__ += 1
+except Exception:
+ __revision__ = None
+except TypeError:
+ __revision__ = None
+
+try:
+ __revision__ += 1
+except LookupError:
+ __revision__ = None
+except IndexError:
+ __revision__ = None
+
+try:
+ __revision__ += 1
+except (LookupError, NameError):
+ __revision__ = None
+except (IndexError, UnboundLocalError):
+ __revision__ = None
+
+try:
+ __revision__ += 1
+except:
+ pass
+except Exception:
+ pass
+
+try:
+ __revision__ += 1
+except TypeError:
+ __revision__ = None
+except:
+ __revision__ = None
+
+try:
+ __revision__ += 1
+except Exception:
+ pass
+except:
+ pass
diff --git a/test/input/func_w0801.py b/test/input/func_w0801.py
new file mode 100644
index 0000000..cd386ff
--- /dev/null
+++ b/test/input/func_w0801.py
@@ -0,0 +1,11 @@
+"""test code similarities
+by defaut docstring are not considered
+"""
+__revision__ = 'id'
+A = 2
+B = 3
+C = A + B
+# need more than X lines to trigger the message
+C *= 2
+A -= B
+# all this should be detected
diff --git a/test/input/func_wrong_encoding.py b/test/input/func_wrong_encoding.py
new file mode 100644
index 0000000..267fa2c
--- /dev/null
+++ b/test/input/func_wrong_encoding.py
@@ -0,0 +1,6 @@
+# -*- coding: UTF-8 -*-
+""" check correct wrong encoding declaration
+"""
+
+__revision__ = 'éééé'
+
diff --git a/test/input/func_yield_outside_func.py b/test/input/func_yield_outside_func.py
new file mode 100644
index 0000000..79d882e
--- /dev/null
+++ b/test/input/func_yield_outside_func.py
@@ -0,0 +1,4 @@
+"""This is gramatically correct, but it's still a SyntaxError"""
+__revision__ = None
+yield 1
+
diff --git a/test/input/indirect1.py b/test/input/indirect1.py
new file mode 100644
index 0000000..eac6242
--- /dev/null
+++ b/test/input/indirect1.py
@@ -0,0 +1,4 @@
+class TotoInterface:
+ def machin(self):
+ raise NotImplementedError
+
diff --git a/test/input/indirect2.py b/test/input/indirect2.py
new file mode 100644
index 0000000..6eefece
--- /dev/null
+++ b/test/input/indirect2.py
@@ -0,0 +1,7 @@
+from indirect1 import TotoInterface
+
+class AbstractToto:
+ __implements__ = TotoInterface
+
+ def helper(self):
+ return 'help'
diff --git a/test/input/indirect3.py b/test/input/indirect3.py
new file mode 100644
index 0000000..dac0853
--- /dev/null
+++ b/test/input/indirect3.py
@@ -0,0 +1,5 @@
+from indirect2 import AbstractToto
+
+class ConcreteToto(AbstractToto):
+ def machin(self):
+ return self.helper()*2
diff --git a/test/input/noext b/test/input/noext
new file mode 100644
index 0000000..8aeda06
--- /dev/null
+++ b/test/input/noext
@@ -0,0 +1,4 @@
+#!/usr/bin/env python
+"""a python file without .py extension"""
+
+__revision__ = None
diff --git a/test/input/similar1 b/test/input/similar1
new file mode 100644
index 0000000..bc88187
--- /dev/null
+++ b/test/input/similar1
@@ -0,0 +1,19 @@
+this file is used
+to check the similar
+command line tool
+
+see the similar2 file which is almost the
+same file as this one.
+more than 4
+identical lines should
+be
+detected
+
+
+héhéhéh
+
+
+
+
+
+Yo !
diff --git a/test/input/similar2 b/test/input/similar2
new file mode 100644
index 0000000..56f9844
--- /dev/null
+++ b/test/input/similar2
@@ -0,0 +1,19 @@
+this file is used
+to check the similar
+command line tool
+
+see the similar1 file which is almost the
+same file as this one.
+more than 4
+identical lines should
+be
+detected
+
+
+hohohoh
+
+
+
+
+
+Yo !
diff --git a/test/input/w0401_cycle.py b/test/input/w0401_cycle.py
new file mode 100644
index 0000000..1fbf880
--- /dev/null
+++ b/test/input/w0401_cycle.py
@@ -0,0 +1,9 @@
+"""w0401 dependancy
+"""
+
+__revision__ = 0
+
+import input.func_w0401
+
+if __revision__:
+ print input
diff --git a/test/input/w0801_same.py b/test/input/w0801_same.py
new file mode 100644
index 0000000..cd386ff
--- /dev/null
+++ b/test/input/w0801_same.py
@@ -0,0 +1,11 @@
+"""test code similarities
+by defaut docstring are not considered
+"""
+__revision__ = 'id'
+A = 2
+B = 3
+C = A + B
+# need more than X lines to trigger the message
+C *= 2
+A -= B
+# all this should be detected
diff --git a/test/messages/2.5_func_newstyle_exceptions.txt b/test/messages/2.5_func_newstyle_exceptions.txt
new file mode 100644
index 0000000..fc3adcb
--- /dev/null
+++ b/test/messages/2.5_func_newstyle_exceptions.txt
@@ -0,0 +1,4 @@
+W: 21:fonctionBof: Exception doesn't inherit from standard "Exception" class
+W: 25:fonctionNew: Exception doesn't inherit from standard "Exception" class
+W: 29:fonctionBof2: Exception doesn't inherit from standard "Exception" class
+W: 33:fonctionNew2: Exception doesn't inherit from standard "Exception" class
diff --git a/test/messages/builtin_module.txt b/test/messages/builtin_module.txt
new file mode 100644
index 0000000..2616c0e
--- /dev/null
+++ b/test/messages/builtin_module.txt
@@ -0,0 +1 @@
+F: 1: ignored builtin module sys
diff --git a/test/messages/func___future___import_not_first_stmt.txt b/test/messages/func___future___import_not_first_stmt.txt
new file mode 100644
index 0000000..ff96b55
--- /dev/null
+++ b/test/messages/func___future___import_not_first_stmt.txt
@@ -0,0 +1 @@
+W: 4: __future__ import is not the first non docstring statement
diff --git a/test/messages/func___name___access.txt b/test/messages/func___name___access.txt
new file mode 100644
index 0000000..51aeeaa
--- /dev/null
+++ b/test/messages/func___name___access.txt
@@ -0,0 +1,3 @@
+E: 11:Aaaa.__init__: Instance of 'Aaaa' has no '__name__' member
+E: 21:NewClass.__init__: Instance of 'NewClass' has no '__name__' member
+
diff --git a/test/messages/func_attrs_definition_order.txt b/test/messages/func_attrs_definition_order.txt
new file mode 100644
index 0000000..895c315
--- /dev/null
+++ b/test/messages/func_attrs_definition_order.txt
@@ -0,0 +1 @@
+E: 9:Aaaa.__init__: Access to member '_var2' before its definition line 10
diff --git a/test/messages/func_backtick_deprecated.txt b/test/messages/func_backtick_deprecated.txt
new file mode 100644
index 0000000..c78baa1
--- /dev/null
+++ b/test/messages/func_backtick_deprecated.txt
@@ -0,0 +1,2 @@
+W: 4: Use of the `` operator
+
diff --git a/test/messages/func_bad_assigment_to_exception_var.txt b/test/messages/func_bad_assigment_to_exception_var.txt
new file mode 100644
index 0000000..9b9bc02
--- /dev/null
+++ b/test/messages/func_bad_assigment_to_exception_var.txt
@@ -0,0 +1,5 @@
+W: 11: Identifier e used to raise an exception is assigned to 1 line 7
+W: 15: Identifier e2 used to raise an exception is assigned to 'yo' line 8
+W: 20:func: Identifier e3 used to raise an exception is assigned to None
+W: 30: Identifier e3 used to raise an exception is assigned to None
+
diff --git a/test/messages/func_base_stmt_without_effect.txt b/test/messages/func_base_stmt_without_effect.txt
new file mode 100644
index 0000000..3fad2d7
--- /dev/null
+++ b/test/messages/func_base_stmt_without_effect.txt
@@ -0,0 +1,6 @@
+W: 17: Statement seems to have no effect
+W: 19: Statement seems to have no effect
+W: 23: Statement seems to have no effect
+W: 26: String statement has no effect
+W: 28: Unnecessary semicolon
+
diff --git a/test/messages/func_base_useless_pass.txt b/test/messages/func_base_useless_pass.txt
new file mode 100644
index 0000000..4398e3f
--- /dev/null
+++ b/test/messages/func_base_useless_pass.txt
@@ -0,0 +1 @@
+W: 9: Unnecessary pass statement
diff --git a/test/messages/func_block_disable_msg.txt b/test/messages/func_block_disable_msg.txt
new file mode 100644
index 0000000..6882b17
--- /dev/null
+++ b/test/messages/func_block_disable_msg.txt
@@ -0,0 +1,10 @@
+E: 26:Foo.meth3: Instance of 'Foo' has no 'blop' member
+E: 36:Foo.meth4: Instance of 'Foo' has no 'blip' member
+E: 46:Foo.meth5: Instance of 'Foo' has no 'blip' member
+E: 61:Foo.meth6: Instance of 'Foo' has no 'blip' member
+E: 72:Foo.meth7: Instance of 'Foo' has no 'blip' member
+E: 75:Foo.meth7: Instance of 'Foo' has no 'blip' member
+E: 77:Foo.meth7: Instance of 'Foo' has no 'blip' member
+E: 83:Foo.meth8: Instance of 'Foo' has no 'blip' member
+E:102:Foo.meth9: Instance of 'Foo' has no 'blip' member
+W: 11:Foo.meth1: Unused argument 'arg'
diff --git a/test/messages/func_class_access_protected_members.txt b/test/messages/func_class_access_protected_members.txt
new file mode 100644
index 0000000..8c7bb02
--- /dev/null
+++ b/test/messages/func_class_access_protected_members.txt
@@ -0,0 +1,3 @@
+W: 19:MyClass.test: Access to a protected member _haha of a client class
+W: 28: Access to a protected member _protected of a client class
+W: 29: Access to a protected member _cls_protected of a client class
diff --git a/test/messages/func_class_members.txt b/test/messages/func_class_members.txt
new file mode 100644
index 0000000..ba2c639
--- /dev/null
+++ b/test/messages/func_class_members.txt
@@ -0,0 +1,3 @@
+E: 16:MyClass.test: Instance of 'MyClass' has no 'incorrect' member
+E: 17:MyClass.test: Instance of 'MyClass' has no 'nonexistent1' member
+E: 18:MyClass.test: Instance of 'MyClass' has no 'nonexistent2' member
diff --git a/test/messages/func_continue_not_in_loop.txt b/test/messages/func_continue_not_in_loop.txt
new file mode 100644
index 0000000..d3a3183
--- /dev/null
+++ b/test/messages/func_continue_not_in_loop.txt
@@ -0,0 +1,2 @@
+E: 8:run: 'continue' not properly in loop
+E: 10:run: 'break' not properly in loop
diff --git a/test/messages/func_dangerous_default.txt b/test/messages/func_dangerous_default.txt
new file mode 100644
index 0000000..21d32e0
--- /dev/null
+++ b/test/messages/func_dangerous_default.txt
@@ -0,0 +1,2 @@
+W: 7:function1: Dangerous default value [] as argument
+W: 11:function2: Dangerous default value HEHE ({}) as argument
diff --git a/test/messages/func_docstring.txt b/test/messages/func_docstring.txt
new file mode 100644
index 0000000..716de4c
--- /dev/null
+++ b/test/messages/func_docstring.txt
@@ -0,0 +1,4 @@
+C: 1: Missing docstring
+C: 5:function1: Missing docstring
+C: 17:AAAA: Missing docstring
+C: 33:AAAA.method1: Missing docstring
diff --git a/test/messages/func_dotted_ancestor.txt b/test/messages/func_dotted_ancestor.txt
new file mode 100644
index 0000000..6e2c6fa
--- /dev/null
+++ b/test/messages/func_dotted_ancestor.txt
@@ -0,0 +1 @@
+R: 8:Aaaa: Too few public methods (0/2)
diff --git a/test/messages/func_e0011.txt b/test/messages/func_e0011.txt
new file mode 100644
index 0000000..55f07b1
--- /dev/null
+++ b/test/messages/func_e0011.txt
@@ -0,0 +1 @@
+E: 1: Unrecognized file option 'bouboule'
diff --git a/test/messages/func_e0012.txt b/test/messages/func_e0012.txt
new file mode 100644
index 0000000..a6d1b69
--- /dev/null
+++ b/test/messages/func_e0012.txt
@@ -0,0 +1 @@
+E: 1: Bad option value 'W04044'
diff --git a/test/messages/func_e0101.txt b/test/messages/func_e0101.txt
new file mode 100644
index 0000000..203cd32
--- /dev/null
+++ b/test/messages/func_e0101.txt
@@ -0,0 +1,2 @@
+E: 10:MyClass.__init__: Explicit return in __init__
+E: 28:MyClass4.__init__: __init__ method is a generator
diff --git a/test/messages/func_e0203.txt b/test/messages/func_e0203.txt
new file mode 100644
index 0000000..869cf22
--- /dev/null
+++ b/test/messages/func_e0203.txt
@@ -0,0 +1,2 @@
+C: 12:Abcd.abcd: Class method should have "cls" as first argument
+
diff --git a/test/messages/func_e0204.txt b/test/messages/func_e0204.txt
new file mode 100644
index 0000000..8e05efe
--- /dev/null
+++ b/test/messages/func_e0204.txt
@@ -0,0 +1,3 @@
+E: 10:Abcd.__init__: Method should have "self" as first argument
+E: 14:Abcd.abdc: Method should have "self" as first argument
+
diff --git a/test/messages/func_e0205.txt b/test/messages/func_e0205.txt
new file mode 100644
index 0000000..1adf408
--- /dev/null
+++ b/test/messages/func_e0205.txt
@@ -0,0 +1,2 @@
+E: 14:Cdef.abcd: An attribute inherited from Abcd hide this method
+
diff --git a/test/messages/func_e0206.txt b/test/messages/func_e0206.txt
new file mode 100644
index 0000000..c15f841
--- /dev/null
+++ b/test/messages/func_e0206.txt
@@ -0,0 +1,3 @@
+E: 6:Abcd: Interface resolved to None is not a class
+E: 13:Cdef: Interface resolved to None is not a class
+
diff --git a/test/messages/func_e0214.txt b/test/messages/func_e0214.txt
new file mode 100644
index 0000000..e3fb5f5
--- /dev/null
+++ b/test/messages/func_e0214.txt
@@ -0,0 +1,2 @@
+C: 11:MetaClass.whatever: Metaclass method should have "mcs" as first argument
+
diff --git a/test/messages/func_e0601.txt b/test/messages/func_e0601.txt
new file mode 100644
index 0000000..321c731
--- /dev/null
+++ b/test/messages/func_e0601.txt
@@ -0,0 +1 @@
+E: 8:function: Using variable 'aaaa' before assignment
diff --git a/test/messages/func_empty_module.txt b/test/messages/func_empty_module.txt
new file mode 100644
index 0000000..4f0d347
--- /dev/null
+++ b/test/messages/func_empty_module.txt
@@ -0,0 +1,2 @@
+C: 1: Missing docstring
+C: 1: Missing required attribute "__revision__"
diff --git a/test/messages/func_exceptions_raise_type_error.txt b/test/messages/func_exceptions_raise_type_error.txt
new file mode 100644
index 0000000..8ac5778
--- /dev/null
+++ b/test/messages/func_exceptions_raise_type_error.txt
@@ -0,0 +1,2 @@
+E: 11: Raising int while only classes, instances or string are allowed
+E: 14: Raising None while only classes, instances or string are allowed \ No newline at end of file
diff --git a/test/messages/func_f0001.txt b/test/messages/func_f0001.txt
new file mode 100644
index 0000000..87c315d
--- /dev/null
+++ b/test/messages/func_f0001.txt
@@ -0,0 +1 @@
+W: 3: Unused import whatever
diff --git a/test/messages/func_f0401.txt b/test/messages/func_f0401.txt
new file mode 100644
index 0000000..1ae5ea4
--- /dev/null
+++ b/test/messages/func_f0401.txt
@@ -0,0 +1,2 @@
+F: 8:function: Unable to import 'tutu.toto' (No module named tutu)
+
diff --git a/test/messages/func_fixme.txt b/test/messages/func_fixme.txt
new file mode 100644
index 0000000..2544ce8
--- /dev/null
+++ b/test/messages/func_fixme.txt
@@ -0,0 +1,2 @@
+W: 5: FIXME: beep
+W: 8: XXX:bop'''
diff --git a/test/messages/func_format.txt b/test/messages/func_format.txt
new file mode 100644
index 0000000..c87d01d
--- /dev/null
+++ b/test/messages/func_format.txt
@@ -0,0 +1,25 @@
+C: 6: Operator not preceded by a space
+notpreceded= 1
+ ^
+C: 7: Operator not followed by a space
+notfollowed =1
+ ^
+C: 8: Operator not followed by a space
+notfollowed <=1
+ ^^
+C: 19: Comma not followed by a space
+aaaa,bbbb = 1,2
+ ^^
+C: 24: More than one statement on a single line
+C: 26: Comma not followed by a space
+ aaaa,bbbb = 1,2
+ ^^
+C: 27: Comma not followed by a space
+ aaaa,bbbb = bbbb,aaaa
+ ^^
+C: 29: Comma not followed by a space
+bbbb = (1,2,3)
+ ^^
+C: 51:other: Operator not preceded by a space
+ funky= funky+2
+ ^
diff --git a/test/messages/func_genexpr_var_scope_py24.txt b/test/messages/func_genexpr_var_scope_py24.txt
new file mode 100644
index 0000000..f599112
--- /dev/null
+++ b/test/messages/func_genexpr_var_scope_py24.txt
@@ -0,0 +1 @@
+E: 6: Undefined variable 'n'
diff --git a/test/messages/func_globals.txt b/test/messages/func_globals.txt
new file mode 100644
index 0000000..40963f2
--- /dev/null
+++ b/test/messages/func_globals.txt
@@ -0,0 +1,6 @@
+E: 27: Undefined variable 'CSTE'
+E: 32:other: Undefined variable 'HOP'
+W: 23:fix_contant: Using the global statement
+W: 26: Using the global statement at the module level
+W: 31:other: Using global for 'HOP' but no assigment is done
+W: 39:define_constant: Global variable 'somevar' undefined at the module level
diff --git a/test/messages/func_i0010.txt b/test/messages/func_i0010.txt
new file mode 100644
index 0000000..2bc4372
--- /dev/null
+++ b/test/messages/func_i0010.txt
@@ -0,0 +1 @@
+I: 1: Unable to consider inline option 'errors-only'
diff --git a/test/messages/func_i0011.txt b/test/messages/func_i0011.txt
new file mode 100644
index 0000000..667e364
--- /dev/null
+++ b/test/messages/func_i0011.txt
@@ -0,0 +1,2 @@
+I: 1: Locally disabling W0404
+
diff --git a/test/messages/func_i0012.txt b/test/messages/func_i0012.txt
new file mode 100644
index 0000000..bf0c90f
--- /dev/null
+++ b/test/messages/func_i0012.txt
@@ -0,0 +1,2 @@
+I: 1: Locally enabling W0404
+
diff --git a/test/messages/func_i0013.txt b/test/messages/func_i0013.txt
new file mode 100644
index 0000000..75d7afd
--- /dev/null
+++ b/test/messages/func_i0013.txt
@@ -0,0 +1 @@
+I: 1: Ignoring entire file
diff --git a/test/messages/func_indent.txt b/test/messages/func_indent.txt
new file mode 100644
index 0000000..aa3645f
--- /dev/null
+++ b/test/messages/func_indent.txt
@@ -0,0 +1,3 @@
+W: 5: Bad indentation. Found 1 spaces, expected 4
+W: 6: Bad indentation. Found 1 spaces, expected 4
+W: 13: Bad indentation. Found 5 spaces, expected 4
diff --git a/test/messages/func_init_vars.txt b/test/messages/func_init_vars.txt
new file mode 100644
index 0000000..44ef6f3
--- /dev/null
+++ b/test/messages/func_init_vars.txt
@@ -0,0 +1 @@
+W: 18:MyClass.met: Attribute 'base_var' defined outside __init__
diff --git a/test/messages/func_interfaces.txt b/test/messages/func_interfaces.txt
new file mode 100644
index 0000000..3b444d7
--- /dev/null
+++ b/test/messages/func_interfaces.txt
@@ -0,0 +1,6 @@
+E: 46:MissingMethod: Missing method 'truc' from IMachin interface
+E: 77:InterfaceCantBeFound: Undefined variable 'undefined'
+E: 88:InterfaceCanNowBeFound: Missing method 'troc' from IMachin interface
+E: 88:InterfaceCanNowBeFound: Missing method 'truc' from IMachin interface
+F: 77:InterfaceCantBeFound: failed to resolve interfaces implemented by InterfaceCantBeFound (undefined)
+W: 71:BadArgument.troc: Arguments number differs from IMachin interface method
diff --git a/test/messages/func_method_could_be_function.txt b/test/messages/func_method_could_be_function.txt
new file mode 100644
index 0000000..1def89e
--- /dev/null
+++ b/test/messages/func_method_could_be_function.txt
@@ -0,0 +1 @@
+R: 16:Toto.function_method: Method could be a function
diff --git a/test/messages/func_method_missing_self.txt b/test/messages/func_method_missing_self.txt
new file mode 100644
index 0000000..861871f
--- /dev/null
+++ b/test/messages/func_method_missing_self.txt
@@ -0,0 +1 @@
+E: 14:MyClass.met: Method has no argument
diff --git a/test/messages/func_method_without_self_but_self_assignment.txt b/test/messages/func_method_without_self_but_self_assignment.txt
new file mode 100644
index 0000000..da5ee02
--- /dev/null
+++ b/test/messages/func_method_without_self_but_self_assignment.txt
@@ -0,0 +1,2 @@
+E: 13:Example.setup: Method has no argument
+E: 15:Example.setup: Undefined variable 'self'
diff --git a/test/messages/func_nameerror_on_string_substitution.txt b/test/messages/func_nameerror_on_string_substitution.txt
new file mode 100644
index 0000000..aab2102
--- /dev/null
+++ b/test/messages/func_nameerror_on_string_substitution.txt
@@ -0,0 +1,2 @@
+E: 5: Using variable 'MSG' before assignment
+E: 8: Using variable 'MSG2' before assignment
diff --git a/test/messages/func_names_imported_from_module.txt b/test/messages/func_names_imported_from_module.txt
new file mode 100644
index 0000000..62b0344
--- /dev/null
+++ b/test/messages/func_names_imported_from_module.txt
@@ -0,0 +1,8 @@
+E: 6: No name 'tutu' in module 'logilab.common'
+E: 7: No name 'toto' in module 'logilab.common'
+E: 11: Module 'logilab.common.modutils' has no 'nonexistant_function' member
+E: 12: Module 'logilab.common.modutils' has no 'another' member
+E: 13: Module 'logilab.common.modutils' has no 'yo' member
+E: 17: Module 'sys' has no 'stdoout' member
+E: 24: No name 'compiile' in module 're'
+E: 24: No name 'findiiter' in module 're'
diff --git a/test/messages/func_newstyle___slots__.txt b/test/messages/func_newstyle___slots__.txt
new file mode 100644
index 0000000..be6b8d1
--- /dev/null
+++ b/test/messages/func_newstyle___slots__.txt
@@ -0,0 +1 @@
+E: 10:HaNonNonNon: Use __slots__ on an old style class
diff --git a/test/messages/func_newstyle_exceptions.txt b/test/messages/func_newstyle_exceptions.txt
new file mode 100644
index 0000000..d92e3bf
--- /dev/null
+++ b/test/messages/func_newstyle_exceptions.txt
@@ -0,0 +1,4 @@
+E: 25:fonctionNew: Raising a new style class
+E: 33:fonctionNew2: Raising a new style class
+W: 21:fonctionBof: Exception doesn't inherit from standard "Exception" class
+W: 29:fonctionBof2: Exception doesn't inherit from standard "Exception" class
diff --git a/test/messages/func_newstyle_property.txt b/test/messages/func_newstyle_property.txt
new file mode 100644
index 0000000..f0ef261
--- /dev/null
+++ b/test/messages/func_newstyle_property.txt
@@ -0,0 +1 @@
+W: 16:HaNonNonNon: Use of "property" on an old style class
diff --git a/test/messages/func_newstyle_super.txt b/test/messages/func_newstyle_super.txt
new file mode 100644
index 0000000..d0cdf78
--- /dev/null
+++ b/test/messages/func_newstyle_super.txt
@@ -0,0 +1,4 @@
+E: 7:Aaaa.hop: Use super on an old style class
+E: 11:Aaaa.__init__: Use super on an old style class
+E: 20:NewAaaa.__init__: Bad first argument 'object' given to super class
+
diff --git a/test/messages/func_nonascii_noencoding.txt b/test/messages/func_nonascii_noencoding.txt
new file mode 100644
index 0000000..dd68a3c
--- /dev/null
+++ b/test/messages/func_nonascii_noencoding.txt
@@ -0,0 +1 @@
+E: 1: Non ascii characters found but no encoding specified (PEP 263)
diff --git a/test/messages/func_r0901.txt b/test/messages/func_r0901.txt
new file mode 100644
index 0000000..d20460e
--- /dev/null
+++ b/test/messages/func_r0901.txt
@@ -0,0 +1,2 @@
+R: 22:Iiii: Too many ancestors (8/7)
+R: 25:Jjjj: Too many ancestors (9/7)
diff --git a/test/messages/func_r0902.txt b/test/messages/func_r0902.txt
new file mode 100644
index 0000000..5dcb669
--- /dev/null
+++ b/test/messages/func_r0902.txt
@@ -0,0 +1 @@
+R: 5:Aaaa: Too many instance attributes (21/7)
diff --git a/test/messages/func_r0903.txt b/test/messages/func_r0903.txt
new file mode 100644
index 0000000..bd4cc2a
--- /dev/null
+++ b/test/messages/func_r0903.txt
@@ -0,0 +1 @@
+R: 4:Aaaa: Too few public methods (1/2)
diff --git a/test/messages/func_r0904.txt b/test/messages/func_r0904.txt
new file mode 100644
index 0000000..76baf72
--- /dev/null
+++ b/test/messages/func_r0904.txt
@@ -0,0 +1 @@
+R: 4:Aaaa: Too many public methods (21/20)
diff --git a/test/messages/func_r0921.txt b/test/messages/func_r0921.txt
new file mode 100644
index 0000000..7e9a442
--- /dev/null
+++ b/test/messages/func_r0921.txt
@@ -0,0 +1 @@
+R: 4:Aaaa: Abstract class not referenced
diff --git a/test/messages/func_r0922.txt b/test/messages/func_r0922.txt
new file mode 100644
index 0000000..70319ee
--- /dev/null
+++ b/test/messages/func_r0922.txt
@@ -0,0 +1 @@
+R: 4:Aaaa: Abstract class is only referenced 1 times
diff --git a/test/messages/func_r0923.txt b/test/messages/func_r0923.txt
new file mode 100644
index 0000000..11ee61d
--- /dev/null
+++ b/test/messages/func_r0923.txt
@@ -0,0 +1 @@
+R: 6:IAaaa: Interface not implemented
diff --git a/test/messages/func_reqattrs.txt b/test/messages/func_reqattrs.txt
new file mode 100644
index 0000000..0563049
--- /dev/null
+++ b/test/messages/func_reqattrs.txt
@@ -0,0 +1 @@
+C: 1: Missing required attribute "__revision__"
diff --git a/test/messages/func_return_outside_func.txt b/test/messages/func_return_outside_func.txt
new file mode 100644
index 0000000..704e917
--- /dev/null
+++ b/test/messages/func_return_outside_func.txt
@@ -0,0 +1 @@
+E: 3: return outside function
diff --git a/test/messages/func_return_yield_mix.txt b/test/messages/func_return_yield_mix.txt
new file mode 100644
index 0000000..6875099
--- /dev/null
+++ b/test/messages/func_return_yield_mix.txt
@@ -0,0 +1 @@
+E: 6:somegen: return with argument inside generator
diff --git a/test/messages/func_return_yield_mix2.txt b/test/messages/func_return_yield_mix2.txt
new file mode 100644
index 0000000..fea8017
--- /dev/null
+++ b/test/messages/func_return_yield_mix2.txt
@@ -0,0 +1 @@
+E: 8:somegen: return with argument inside generator
diff --git a/test/messages/func_scope_regrtest.txt b/test/messages/func_scope_regrtest.txt
new file mode 100644
index 0000000..27ae207
--- /dev/null
+++ b/test/messages/func_scope_regrtest.txt
@@ -0,0 +1,2 @@
+E: 12:Well.Sub: Undefined variable 'Data'
+E: 15:Well.func: Undefined variable 'Sub'
diff --git a/test/messages/func_syntax_error.txt b/test/messages/func_syntax_error.txt
new file mode 100644
index 0000000..deee535
--- /dev/null
+++ b/test/messages/func_syntax_error.txt
@@ -0,0 +1,2 @@
+E: 1: invalid syntax
+
diff --git a/test/messages/func_toolonglines.txt b/test/messages/func_toolonglines.txt
new file mode 100644
index 0000000..29b16c6
--- /dev/null
+++ b/test/messages/func_toolonglines.txt
@@ -0,0 +1,2 @@
+C: 1: Line too long (90/80)
+C: 2: Line too long (94/80)
diff --git a/test/messages/func_typecheck_callfunc_assigment.txt b/test/messages/func_typecheck_callfunc_assigment.txt
new file mode 100644
index 0000000..96ad43e
--- /dev/null
+++ b/test/messages/func_typecheck_callfunc_assigment.txt
@@ -0,0 +1,2 @@
+E: 20: Assigning to function call which doesn't return
+W: 28: Assigning to function call which only returns None
diff --git a/test/messages/func_typecheck_getattr.txt b/test/messages/func_typecheck_getattr.txt
new file mode 100644
index 0000000..cd9f916
--- /dev/null
+++ b/test/messages/func_typecheck_getattr.txt
@@ -0,0 +1,10 @@
+E: 25:Client.__init__: Class 'Provider' has no 'cattribute' member
+E: 35:Client.use_method: Instance of 'Provider' has no 'hophophop' member
+E: 40:Client.use_attr: Instance of 'Provider' has no 'attribute' member
+E: 52:Client.test_bt_types: Instance of 'list' has no 'apppend' member
+E: 54:Client.test_bt_types: Instance of 'dict' has no 'set' member
+E: 56:Client.test_bt_types: Instance of 'tuple' has no 'append' member
+E: 58:Client.test_bt_types: Instance of 'str' has no 'loower' member
+E: 60:Client.test_bt_types: Instance of 'unicode' has no 'loower' member
+E: 62:Client.test_bt_types: Instance of 'int' has no 'whatever' member
+E: 66: Instance of 'int' has no 'lower' member (but some types could not be inferred)
diff --git a/test/messages/func_typecheck_non_callable_call.txt b/test/messages/func_typecheck_non_callable_call.txt
new file mode 100644
index 0000000..0218074
--- /dev/null
+++ b/test/messages/func_typecheck_non_callable_call.txt
@@ -0,0 +1,6 @@
+E: 10: __revision__ is not callable
+E: 29: INSTANCE is not callable
+E: 31: LIST is not callable
+E: 33: DICT is not callable
+E: 35: TUPLE is not callable
+E: 37: INT is not callable
diff --git a/test/messages/func_undefined_var.txt b/test/messages/func_undefined_var.txt
new file mode 100644
index 0000000..07666f7
--- /dev/null
+++ b/test/messages/func_undefined_var.txt
@@ -0,0 +1,5 @@
+E: 8: Undefined variable 'unknown'
+E: 14:in_method: Undefined variable 'nomoreknown'
+E: 22:bad_default: Undefined variable 'unknown2'
+E: 25:bad_default: Undefined variable 'xxxx'
+E: 26:bad_default: Undefined variable 'xxxx'
diff --git a/test/messages/func_unknown_encoding.txt b/test/messages/func_unknown_encoding.txt
new file mode 100644
index 0000000..648af39
--- /dev/null
+++ b/test/messages/func_unknown_encoding.txt
@@ -0,0 +1 @@
+E: 1: Unknown encoding specified (IBO-8859-1)
diff --git a/test/messages/func_unreachable.txt b/test/messages/func_unreachable.txt
new file mode 100644
index 0000000..bb25be0
--- /dev/null
+++ b/test/messages/func_unreachable.txt
@@ -0,0 +1,3 @@
+W: 8:func1: Unreachable code
+W: 14:func2: Unreachable code
+W: 21:func3: Unreachable code
diff --git a/test/messages/func_use_for_or_listcomp_var.txt b/test/messages/func_use_for_or_listcomp_var.txt
new file mode 100644
index 0000000..14d61ad
--- /dev/null
+++ b/test/messages/func_use_for_or_listcomp_var.txt
@@ -0,0 +1,3 @@
+W: 6: Using possibly undefined loop variable 'C'
+W: 15: Using possibly undefined loop variable 'var1'
+
diff --git a/test/messages/func_variables_unused_name_from_wilcard_import.txt b/test/messages/func_variables_unused_name_from_wilcard_import.txt
new file mode 100644
index 0000000..4768afa
--- /dev/null
+++ b/test/messages/func_variables_unused_name_from_wilcard_import.txt
@@ -0,0 +1,5 @@
+W: 2: Unused import NonRegr from wildcard import
+W: 2: Unused import os from wildcard import
+W: 2: Unused import sys from wildcard import
+W: 2: Wildcard import input.func_w0611
+
diff --git a/test/messages/func_w0101.txt b/test/messages/func_w0101.txt
new file mode 100644
index 0000000..c42ec2c
--- /dev/null
+++ b/test/messages/func_w0101.txt
@@ -0,0 +1 @@
+R: 6:stupid_function: Too many return statements (11/6)
diff --git a/test/messages/func_w0102.txt b/test/messages/func_w0102.txt
new file mode 100644
index 0000000..40b6190
--- /dev/null
+++ b/test/messages/func_w0102.txt
@@ -0,0 +1,5 @@
+E: 15:AAAA.method2: method already defined line 12
+E: 18:AAAA: class already defined line 5
+E: 32:func2: function already defined line 29
+E: 51:exclusive_func2: function already defined line 45
+W: 34:func2: Redefining name '__revision__' from outer scope (line 3)
diff --git a/test/messages/func_w0103.txt b/test/messages/func_w0103.txt
new file mode 100644
index 0000000..0d6da42
--- /dev/null
+++ b/test/messages/func_w0103.txt
@@ -0,0 +1 @@
+R: 6:stupid_function: Too many arguments (9/5)
diff --git a/test/messages/func_w0104.txt b/test/messages/func_w0104.txt
new file mode 100644
index 0000000..71f6f62
--- /dev/null
+++ b/test/messages/func_w0104.txt
@@ -0,0 +1 @@
+R: 6:stupid_function: Too many local variables (16/15)
diff --git a/test/messages/func_w0105.txt b/test/messages/func_w0105.txt
new file mode 100644
index 0000000..d664dd4
--- /dev/null
+++ b/test/messages/func_w0105.txt
@@ -0,0 +1 @@
+R: 6:stupid_function: Too many statements (55/50)
diff --git a/test/messages/func_w0109.txt b/test/messages/func_w0109.txt
new file mode 100644
index 0000000..4685d38
--- /dev/null
+++ b/test/messages/func_w0109.txt
@@ -0,0 +1 @@
+C: 6:function: Empty docstring
diff --git a/test/messages/func_w0110.txt b/test/messages/func_w0110.txt
new file mode 100644
index 0000000..28f48aa
--- /dev/null
+++ b/test/messages/func_w0110.txt
@@ -0,0 +1 @@
+C: 8:a: Invalid name "a" (should match [a-z_][a-z0-9_]{2,30}$)
diff --git a/test/messages/func_w0111.txt b/test/messages/func_w0111.txt
new file mode 100644
index 0000000..b2d794b
--- /dev/null
+++ b/test/messages/func_w0111.txt
@@ -0,0 +1 @@
+C: 8:baz: Black listed name "baz"
diff --git a/test/messages/func_w0112.txt b/test/messages/func_w0112.txt
new file mode 100644
index 0000000..19b2da5
--- /dev/null
+++ b/test/messages/func_w0112.txt
@@ -0,0 +1 @@
+R: 6:stupid_function: Too many branches (15/12)
diff --git a/test/messages/func_w0122.txt b/test/messages/func_w0122.txt
new file mode 100644
index 0000000..1522cac
--- /dev/null
+++ b/test/messages/func_w0122.txt
@@ -0,0 +1,5 @@
+W: 5: Use of the exec statement
+W: 6: Use of the exec statement
+W: 8: Use of the exec statement
+W: 12:func: Use of the exec statement
+
diff --git a/test/messages/func_w0133.txt b/test/messages/func_w0133.txt
new file mode 100644
index 0000000..f2929ae
--- /dev/null
+++ b/test/messages/func_w0133.txt
@@ -0,0 +1,9 @@
+C: 8:Run.B: Invalid name "B" (should match [A-Z_][a-zA-Z0-9]+$)
+C: 12:Run: Invalid name "bBb" (should match [a-z_][a-z0-9_]{2,30}$)
+C: 26:HOHOHOHO: Invalid name "HOHOHOHO" (should match [a-z_][a-z0-9_]{2,30}$)
+C: 28:HOHOHOHO: Invalid name "HIHIHI" (should match [a-z_][a-z0-9_]{2,30}$)
+C: 31:xyz: Invalid name "xyz" (should match [A-Z_][a-zA-Z0-9]+$)
+C: 36:xyz.Youplapoum: Invalid name "Youplapoum" (should match [a-z_][a-z0-9_]{2,30}$)
+C: 46: Invalid name "benpasceluila" (should match (([A-Z_][A-Z1-9_]*)|(__.*__))$)
+C: 52:Correct.__init__: Invalid name "_Ca_va_Pas" (should match [a-z_][a-z0-9_]{2,30}$)
+W: 8:Run.B: Unused variable 'B'
diff --git a/test/messages/func_w0151.txt b/test/messages/func_w0151.txt
new file mode 100644
index 0000000..dae33d9
--- /dev/null
+++ b/test/messages/func_w0151.txt
@@ -0,0 +1,2 @@
+W: 4: Used builtin function 'apply'
+W: 6: Used builtin function 'map'
diff --git a/test/messages/func_w0152.txt b/test/messages/func_w0152.txt
new file mode 100644
index 0000000..ddf2d0b
--- /dev/null
+++ b/test/messages/func_w0152.txt
@@ -0,0 +1,3 @@
+W: 5: Used * or ** magic
+W: 14: Used * or ** magic
+
diff --git a/test/messages/func_w0202.txt b/test/messages/func_w0202.txt
new file mode 100644
index 0000000..d10e5bc
--- /dev/null
+++ b/test/messages/func_w0202.txt
@@ -0,0 +1,3 @@
+W: 8:Abcd.method1: Static method with 'self' as first argument
+W: 12:Abcd.method2: Static method with 'cls' as first argument
+
diff --git a/test/messages/func_w0205.txt b/test/messages/func_w0205.txt
new file mode 100644
index 0000000..4b36dab
--- /dev/null
+++ b/test/messages/func_w0205.txt
@@ -0,0 +1,2 @@
+W: 22:Cdef.abcd: Signature differs from overridden method
+
diff --git a/test/messages/func_w0223.txt b/test/messages/func_w0223.txt
new file mode 100644
index 0000000..fc735da
--- /dev/null
+++ b/test/messages/func_w0223.txt
@@ -0,0 +1,2 @@
+W: 22:Concret: Method 'bbbb' is abstract in class 'Abstract' but is not overridden
+
diff --git a/test/messages/func_w0231.txt b/test/messages/func_w0231.txt
new file mode 100644
index 0000000..84e176d
--- /dev/null
+++ b/test/messages/func_w0231.txt
@@ -0,0 +1,2 @@
+W: 19:CCCC: Class has no __init__ method
+W: 26:ZZZZ.__init__: __init__ method from base class 'BBBB' is not called
diff --git a/test/messages/func_w0233.txt b/test/messages/func_w0233.txt
new file mode 100644
index 0000000..edf0e45
--- /dev/null
+++ b/test/messages/func_w0233.txt
@@ -0,0 +1 @@
+W: 12:AAAA.__init__: __init__ method from a non direct base class 'BBBBMixin' is called
diff --git a/test/messages/func_w0302.txt b/test/messages/func_w0302.txt
new file mode 100644
index 0000000..58f65b3
--- /dev/null
+++ b/test/messages/func_w0302.txt
@@ -0,0 +1,2 @@
+C: 1: Too many lines in module (1016)
+
diff --git a/test/messages/func_w0312.txt b/test/messages/func_w0312.txt
new file mode 100644
index 0000000..917e8d0
--- /dev/null
+++ b/test/messages/func_w0312.txt
@@ -0,0 +1,2 @@
+W: 10: Found indentation with tabs instead of spaces
+W: 11: Found indentation with tabs instead of spaces
diff --git a/test/messages/func_w0331.txt b/test/messages/func_w0331.txt
new file mode 100644
index 0000000..8134a32
--- /dev/null
+++ b/test/messages/func_w0331.txt
@@ -0,0 +1 @@
+W: 6: Use of the <> operator
diff --git a/test/messages/func_w0332.txt b/test/messages/func_w0332.txt
new file mode 100644
index 0000000..b8984ad
--- /dev/null
+++ b/test/messages/func_w0332.txt
@@ -0,0 +1 @@
+W: 4: Use l as long integer identifier
diff --git a/test/messages/func_w0401.txt b/test/messages/func_w0401.txt
new file mode 100644
index 0000000..74b14dc
--- /dev/null
+++ b/test/messages/func_w0401.txt
@@ -0,0 +1,2 @@
+R: 1: Cyclic import (input.func_w0401 -> input.w0401_cycle)
+W: 6: Redefining built-in 'input'
diff --git a/test/messages/func_w0402.txt b/test/messages/func_w0402.txt
new file mode 100644
index 0000000..cf06fc4
--- /dev/null
+++ b/test/messages/func_w0402.txt
@@ -0,0 +1,2 @@
+W: 5: Wildcard import input.func_fixme
+
diff --git a/test/messages/func_w0403.txt b/test/messages/func_w0403.txt
new file mode 100644
index 0000000..ef511f7
--- /dev/null
+++ b/test/messages/func_w0403.txt
@@ -0,0 +1 @@
+W: 8: Uses of a deprecated module 'Bastion'
diff --git a/test/messages/func_w0404.txt b/test/messages/func_w0404.txt
new file mode 100644
index 0000000..311056d
--- /dev/null
+++ b/test/messages/func_w0404.txt
@@ -0,0 +1 @@
+W: 6: Relative import 'func_w0302'
diff --git a/test/messages/func_w0405.txt b/test/messages/func_w0405.txt
new file mode 100644
index 0000000..9555dfd
--- /dev/null
+++ b/test/messages/func_w0405.txt
@@ -0,0 +1,4 @@
+W: 9: Reimport 'os' (imported line 6)
+W: 16: Reimport 'exists' (imported line 7)
+W: 21:func: Reimport 'os' (imported line 6)
+W: 23:func: Reimport 're' (imported line 10)
diff --git a/test/messages/func_w0406.txt b/test/messages/func_w0406.txt
new file mode 100644
index 0000000..a1440b9
--- /dev/null
+++ b/test/messages/func_w0406.txt
@@ -0,0 +1,2 @@
+W: 5: Module import itself
+W: 5: Relative import 'func_w0406'
diff --git a/test/messages/func_w0611.txt b/test/messages/func_w0611.txt
new file mode 100644
index 0000000..10952da
--- /dev/null
+++ b/test/messages/func_w0611.txt
@@ -0,0 +1 @@
+W: 4: Unused import os
diff --git a/test/messages/func_w0612.txt b/test/messages/func_w0612.txt
new file mode 100644
index 0000000..b2d9cbb
--- /dev/null
+++ b/test/messages/func_w0612.txt
@@ -0,0 +1 @@
+W: 8:function: Unused variable 'aaaa'
diff --git a/test/messages/func_w0613.txt b/test/messages/func_w0613.txt
new file mode 100644
index 0000000..b658b54
--- /dev/null
+++ b/test/messages/func_w0613.txt
@@ -0,0 +1,2 @@
+W: 7:function: Unused argument 'arg'
+W: 14:AAAA.method: Unused argument 'arg'
diff --git a/test/messages/func_w0622.txt b/test/messages/func_w0622.txt
new file mode 100644
index 0000000..7191347
--- /dev/null
+++ b/test/messages/func_w0622.txt
@@ -0,0 +1,2 @@
+W: 8:function: Redefining built-in 'type'
+W: 11: Redefining built-in 'map'
diff --git a/test/messages/func_w0701.txt b/test/messages/func_w0701.txt
new file mode 100644
index 0000000..677443b
--- /dev/null
+++ b/test/messages/func_w0701.txt
@@ -0,0 +1,3 @@
+W: 8:function1: Raising a string exception
+W: 12:function2: Raising a string exception
+
diff --git a/test/messages/func_w0702.txt b/test/messages/func_w0702.txt
new file mode 100644
index 0000000..cd4ba27
--- /dev/null
+++ b/test/messages/func_w0702.txt
@@ -0,0 +1 @@
+W: 10: No exception type(s) specified
diff --git a/test/messages/func_w0703.txt b/test/messages/func_w0703.txt
new file mode 100644
index 0000000..e37197a
--- /dev/null
+++ b/test/messages/func_w0703.txt
@@ -0,0 +1 @@
+W: 8: Catch "Exception"
diff --git a/test/messages/func_w0704.txt b/test/messages/func_w0704.txt
new file mode 100644
index 0000000..1eca794
--- /dev/null
+++ b/test/messages/func_w0704.txt
@@ -0,0 +1 @@
+W: 8: Except doesn't do anything
diff --git a/test/messages/func_w0705.txt b/test/messages/func_w0705.txt
new file mode 100644
index 0000000..303e739
--- /dev/null
+++ b/test/messages/func_w0705.txt
@@ -0,0 +1,5 @@
+E: 10: Bad except clauses order (Exception is an ancestor class of TypeError)
+E: 17: Bad except clauses order (LookupError is an ancestor class of IndexError)
+E: 24: Bad except clauses order (LookupError is an ancestor class of IndexError)
+E: 24: Bad except clauses order (NameError is an ancestor class of UnboundLocalError)
+E: 27: Bad except clauses order (empty except clause should always appears last) \ No newline at end of file
diff --git a/test/messages/func_w0801.txt b/test/messages/func_w0801.txt
new file mode 100644
index 0000000..203ce92
--- /dev/null
+++ b/test/messages/func_w0801.txt
@@ -0,0 +1,11 @@
+R: 1: Similar lines in 2 files
+==input.func_w0801:3
+==input.w0801_same:3
+__revision__ = 'id'
+A = 2
+B = 3
+C = A + B
+# need more than X lines to trigger the message
+C *= 2
+A -= B
+# all this should be detected
diff --git a/test/messages/func_wrong_encoding.txt b/test/messages/func_wrong_encoding.txt
new file mode 100644
index 0000000..10123a1
--- /dev/null
+++ b/test/messages/func_wrong_encoding.txt
@@ -0,0 +1 @@
+E: 1: Wrong encoding specified (UTF-8)
diff --git a/test/messages/func_yield_outside_func.txt b/test/messages/func_yield_outside_func.txt
new file mode 100644
index 0000000..a5f6c12
--- /dev/null
+++ b/test/messages/func_yield_outside_func.txt
@@ -0,0 +1 @@
+E: 3: yield outside function
diff --git a/test/messages/nonexistant.txt b/test/messages/nonexistant.txt
new file mode 100644
index 0000000..1a14b6e
--- /dev/null
+++ b/test/messages/nonexistant.txt
@@ -0,0 +1 @@
+F: 1: No module named nonexistant
diff --git a/test/messages/nonexistant.txt2 b/test/messages/nonexistant.txt2
new file mode 100644
index 0000000..1ba6167
--- /dev/null
+++ b/test/messages/nonexistant.txt2
@@ -0,0 +1 @@
+F: 1: No module named input/nonexistant
diff --git a/test/regrtest.py b/test/regrtest.py
new file mode 100644
index 0000000..1e660d2
--- /dev/null
+++ b/test/regrtest.py
@@ -0,0 +1,142 @@
+# Copyright (c) 2005 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.,
+# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+"""non regression tests for pylint, which requires a too specific configuration
+to be incorporated in the automatic functionnal test framework
+"""
+
+import sys
+import os
+from os.path import abspath, join
+
+from logilab.common.testlib import TestCase, unittest_main
+
+from utils import TestReporter
+
+from pylint.lint import PyLinter
+from pylint import checkers
+
+test_reporter = TestReporter()
+linter = PyLinter()
+linter.set_reporter(test_reporter)
+linter.disable_message_category('I')
+linter.config.persistent = 0
+checkers.initialize(linter)
+
+sys.path.insert(1, abspath('regrtest_data'))
+
+class NonRegrTC(TestCase):
+ def setUp(self):
+ """call reporter.finalize() to cleanup
+ pending messages if a test finished badly
+ """
+ linter.reporter.finalize()
+
+ def test_package___path___manipulation(self):
+ linter.check('package.__init__')
+ got = linter.reporter.finalize().strip()
+ self.failUnlessEqual(got, '')
+
+ def test_package___init___precedence(self):
+ linter.check('precedence_test')
+ got = linter.reporter.finalize().strip()
+ self.failUnlessEqual(got, '')
+
+ def test_check_package___init__(self):
+ for variation in ('package.__init__', 'regrtest_data/package/__init__.py'):
+ linter.check(variation)
+ got = linter.reporter.finalize().strip()
+ checked = linter.stats['by_module'].keys()
+ self.failUnlessEqual(checked, ['package.__init__'],
+ '%s: %s' % (variation, checked))
+ cwd = os.getcwd()
+ os.chdir('regrtest_data/package')
+ sys.path.insert(0, '')
+ try:
+ for variation in ('__init__', '__init__.py'):
+ linter.check(variation)
+ got = linter.reporter.finalize().strip()
+ checked = linter.stats['by_module'].keys()
+ self.failUnlessEqual(checked, ['__init__'],
+ '%s: %s' % (variation, checked))
+ finally:
+ sys.path.pop(0)
+ os.chdir(cwd)
+
+ def test_gtk_import(self):
+ try:
+ import gtk
+ except ImportError:
+ self.skip('test skipped: gtk is not available')
+ except RuntimeError: # RuntimeError when missing display
+ self.skip('no display, can\'t run this test')
+ linter.check('regrtest_data/pygtk_import.py')
+ got = linter.reporter.finalize().strip()
+ self.failUnlessEqual(got, '')
+
+ def test_numarray_inference(self):
+ try:
+ from numarray import random_array
+ except ImportError:
+ self.skip('test skipped: numarray.random_array is not available')
+ linter.check('regrtest_data/numarray_inf.py')
+ got = linter.reporter.finalize().strip()
+ self.failUnlessEqual(got, "E: 5: Instance of 'int' has no 'astype' member (but some types could not be inferred)")
+
+ def test_numarray_import(self):
+ try:
+ import numarray
+ except ImportError:
+ self.skip('test skipped: numarray is not available')
+ linter.check('regrtest_data/numarray_import.py')
+ got = linter.reporter.finalize().strip()
+ self.failUnlessEqual(got, '')
+
+ def test_socketerror_import(self):
+ linter.check('regrtest_data/socketerror_import.py')
+ got = linter.reporter.finalize().strip()
+ self.failUnlessEqual(got, '')
+
+ def test_class__doc__usage(self):
+ linter.check('regrtest_data/classdoc_usage.py')
+ got = linter.reporter.finalize().strip()
+ self.failUnlessEqual(got, '')
+
+ def test_package_import_relative_subpackage_no_attribute_error(self):
+ linter.check('import_package_subpackage_module')
+ got = linter.reporter.finalize().strip()
+ self.failUnlessEqual(got, '')
+
+ def test_module_global_crash(self):
+ linter.check('regrtest_data/module_global.py')
+ got = linter.reporter.finalize().strip()
+ self.failUnlessEqual(got, '')
+
+ def test_decimal_inference(self):
+ linter.check('regrtest_data/decimal_inference.py')
+ got = linter.reporter.finalize().strip()
+ self.failUnlessEqual(got, "")
+
+ def test_descriptor_crash(self):
+ for fname in os.listdir('regrtest_data'):
+ if fname.endswith('_crash.py'):
+ linter.check(join('regrtest_data', fname))
+ linter.reporter.finalize().strip()
+
+ def test_try_finally_disable_msg_crash(self):
+ linter.check(join('regrtest_data', 'try_finally_disable_msg_crash'))
+
+if __name__ == '__main__':
+ unittest_main()
diff --git a/test/regrtest_data/application_crash.py b/test/regrtest_data/application_crash.py
new file mode 100644
index 0000000..6e6044a
--- /dev/null
+++ b/test/regrtest_data/application_crash.py
@@ -0,0 +1,12 @@
+class ErudiPublisher:
+ def __init__(self, config):
+ self.url_resolver = self.select_component('urlpublisher')
+
+ def select_component(self, cid, *args, **kwargs):
+ try:
+ return self.select(self.registry_objects('components', cid), *args, **kwargs)
+ except NoSelectableObject:
+ return
+
+ def main_publish(self, path, req):
+ ctrlid = self.url_resolver.process(req, path)
diff --git a/test/regrtest_data/classdoc_usage.py b/test/regrtest_data/classdoc_usage.py
new file mode 100644
index 0000000..ae8b9fe
--- /dev/null
+++ b/test/regrtest_data/classdoc_usage.py
@@ -0,0 +1,17 @@
+"""ds"""
+
+__revision__ = None
+
+class SomeClass:
+ """cds"""
+ doc = __doc__
+
+ def __init__(self):
+ """only to make pylint happier"""
+
+ def please(self):
+ """public method 1/2"""
+
+ def besilent(self):
+ """public method 2/2"""
+
diff --git a/test/regrtest_data/decimal_inference.py b/test/regrtest_data/decimal_inference.py
new file mode 100644
index 0000000..1b97f60
--- /dev/null
+++ b/test/regrtest_data/decimal_inference.py
@@ -0,0 +1,10 @@
+"""hum E1011 on .prec member is justifiable since Context instance are built
+using setattr/locals :(
+
+2007/02/17 update: .prec attribute is now detected by astng :o)
+"""
+import decimal
+
+decimal.getcontext().prec = 200
+print decimal.getcontext().prec
+
diff --git a/test/regrtest_data/descriptor_crash.py b/test/regrtest_data/descriptor_crash.py
new file mode 100644
index 0000000..4b3adcc
--- /dev/null
+++ b/test/regrtest_data/descriptor_crash.py
@@ -0,0 +1,20 @@
+# -*- coding: iso-8859-1 -*-
+
+import urllib
+
+class Page(object):
+ _urlOpen = staticmethod(urllib.urlopen)
+
+ def getPage(self, url):
+ handle = self._urlOpen(url)
+ data = handle.read()
+ handle.close()
+ return data
+ #_getPage
+
+#Page
+
+if __name__ == "__main__":
+ import sys
+ p = Page()
+ print p.getPage(sys.argv[1])
diff --git a/test/regrtest_data/import_package_subpackage_module.py b/test/regrtest_data/import_package_subpackage_module.py
new file mode 100644
index 0000000..b5fb219
--- /dev/null
+++ b/test/regrtest_data/import_package_subpackage_module.py
@@ -0,0 +1,49 @@
+# pylint: disable-msg=I0011,C0301,W0611
+"""I found some of my scripts trigger off an AttributeError in pylint
+0.8.1 (with common 0.12.0 and astng 0.13.1).
+
+Traceback (most recent call last):
+ File "/usr/bin/pylint", line 4, in ?
+ lint.Run(sys.argv[1:])
+ File "/usr/lib/python2.4/site-packages/pylint/lint.py", line 729, in __init__
+ linter.check(args)
+ File "/usr/lib/python2.4/site-packages/pylint/lint.py", line 412, in check
+ self.check_file(filepath, modname, checkers)
+ File "/usr/lib/python2.4/site-packages/pylint/lint.py", line 426, in check_file
+ astng = self._check_file(filepath, modname, checkers)
+ File "/usr/lib/python2.4/site-packages/pylint/lint.py", line 450, in _check_file
+ self.check_astng_module(astng, checkers)
+ File "/usr/lib/python2.4/site-packages/pylint/lint.py", line 494, in check_astng_module
+ self.astng_events(astng, [checker for checker in checkers
+ File "/usr/lib/python2.4/site-packages/pylint/lint.py", line 511, in astng_events
+ self.astng_events(child, checkers, _reversed_checkers)
+ File "/usr/lib/python2.4/site-packages/pylint/lint.py", line 511, in astng_events
+ self.astng_events(child, checkers, _reversed_checkers)
+ File "/usr/lib/python2.4/site-packages/pylint/lint.py", line 508, in astng_events
+ checker.visit(astng)
+ File "/usr/lib/python2.4/site-packages/logilab/astng/utils.py", line 84, in visit
+ method(node)
+ File "/usr/lib/python2.4/site-packages/pylint/checkers/variables.py", line 295, in visit_import
+ self._check_module_attrs(node, module, name_parts[1:])
+ File "/usr/lib/python2.4/site-packages/pylint/checkers/variables.py", line 357, in _check_module_attrs
+ self.add_message('E0611', args=(name, module.name),
+AttributeError: Import instance has no attribute 'name'
+
+
+You can reproduce it by:
+(1) create package structure like the following:
+
+package/
+ __init__.py
+ subpackage/
+ __init__.py
+ module.py
+
+(2) in package/__init__.py write:
+
+import subpackage
+
+(3) run pylint with a script importing package.subpackage.module.
+"""
+__revision__ = '$Id: import_package_subpackage_module.py,v 1.1 2005-11-10 16:08:54 syt Exp $'
+import package.subpackage.module
diff --git a/test/regrtest_data/module_global.py b/test/regrtest_data/module_global.py
new file mode 100644
index 0000000..54fd46d
--- /dev/null
+++ b/test/regrtest_data/module_global.py
@@ -0,0 +1,7 @@
+# pylint: disable-msg=W0603,W0601,W0604,E0602,W0104
+"""was causing infinite recursion
+"""
+__revision__ = 1
+
+global bar
+bar.foo
diff --git a/test/regrtest_data/numarray_import.py b/test/regrtest_data/numarray_import.py
new file mode 100644
index 0000000..3f0be8b
--- /dev/null
+++ b/test/regrtest_data/numarray_import.py
@@ -0,0 +1,7 @@
+"""#10077"""
+
+__revision__ = 1
+
+from numarray import zeros
+
+zeros()
diff --git a/test/regrtest_data/numarray_inf.py b/test/regrtest_data/numarray_inf.py
new file mode 100644
index 0000000..4ea22a9
--- /dev/null
+++ b/test/regrtest_data/numarray_inf.py
@@ -0,0 +1,5 @@
+"""#3216"""
+
+import numarray as na
+import numarray.random_array as nar
+IM16 = nar.randint(0, 256, 300).astype(na.UInt8)
diff --git a/test/regrtest_data/package/AudioTime.py b/test/regrtest_data/package/AudioTime.py
new file mode 100644
index 0000000..a1fde96
--- /dev/null
+++ b/test/regrtest_data/package/AudioTime.py
@@ -0,0 +1,3 @@
+"""test preceeded by the AudioTime class in __init__.py"""
+
+__revision__ = 0
diff --git a/test/regrtest_data/package/__init__.py b/test/regrtest_data/package/__init__.py
new file mode 100644
index 0000000..9e1ebfa
--- /dev/null
+++ b/test/regrtest_data/package/__init__.py
@@ -0,0 +1,14 @@
+# pylint: disable-msg=R0903,W0403
+"""package's __init__ file"""
+
+__revision__ = 0
+
+# E0602 - Undefined variable '__path__'
+__path__ += "folder"
+
+class AudioTime(object):
+ """test precedence over the AudioTime submodule"""
+
+ DECIMAL = 3
+
+import subpackage
diff --git a/test/regrtest_data/package/subpackage/__init__.py b/test/regrtest_data/package/subpackage/__init__.py
new file mode 100644
index 0000000..dc4782e
--- /dev/null
+++ b/test/regrtest_data/package/subpackage/__init__.py
@@ -0,0 +1 @@
+"""package.subpackage"""
diff --git a/test/regrtest_data/package/subpackage/module.py b/test/regrtest_data/package/subpackage/module.py
new file mode 100644
index 0000000..4b7244b
--- /dev/null
+++ b/test/regrtest_data/package/subpackage/module.py
@@ -0,0 +1 @@
+"""package.subpackage.module"""
diff --git a/test/regrtest_data/precedence_test.py b/test/regrtest_data/precedence_test.py
new file mode 100644
index 0000000..087b5cf
--- /dev/null
+++ b/test/regrtest_data/precedence_test.py
@@ -0,0 +1,21 @@
+"""
+ # package/__init__.py
+ class AudioTime(object):
+ DECIMAL = 3
+
+ # package/AudioTime.py
+ class AudioTime(object):
+ pass
+
+ # test.py
+ from package import AudioTime
+ # E0611 - No name 'DECIMAL' in module 'AudioTime.AudioTime'
+ print AudioTime.DECIMAL
+
+"""
+
+__revision__ = 0
+
+from package import AudioTime
+
+print AudioTime.DECIMAL
diff --git a/test/regrtest_data/pygtk_import.py b/test/regrtest_data/pygtk_import.py
new file mode 100644
index 0000000..4ac9560
--- /dev/null
+++ b/test/regrtest_data/pygtk_import.py
@@ -0,0 +1,14 @@
+#pylint: disable-msg=R0903,R0904
+"""#10026"""
+__revision__ = 1
+from gtk import VBox
+import gtk
+
+class FooButton(gtk.Button):
+ """extend gtk.Button"""
+ def extend(self):
+ """hop"""
+ print self
+
+print gtk.Button
+print VBox
diff --git a/test/regrtest_data/socketerror_import.py b/test/regrtest_data/socketerror_import.py
new file mode 100644
index 0000000..37310cf
--- /dev/null
+++ b/test/regrtest_data/socketerror_import.py
@@ -0,0 +1,6 @@
+"""ds"""
+
+__revision__ = '$Id: socketerror_import.py,v 1.2 2005-12-28 14:58:22 syt Exp $'
+
+from socket import error
+print error
diff --git a/test/regrtest_data/try_finally_disable_msg_crash.py b/test/regrtest_data/try_finally_disable_msg_crash.py
new file mode 100644
index 0000000..9628191
--- /dev/null
+++ b/test/regrtest_data/try_finally_disable_msg_crash.py
@@ -0,0 +1,5 @@
+try:
+ pass
+finally:
+ # pylint: disable-msg=W0201
+ pass
diff --git a/test/rpythoninput/__init__.py b/test/rpythoninput/__init__.py
new file mode 100644
index 0000000..60e92b7
--- /dev/null
+++ b/test/rpythoninput/__init__.py
@@ -0,0 +1 @@
+"""test"""
diff --git a/test/rpythoninput/func_genexpr.py b/test/rpythoninput/func_genexpr.py
new file mode 100644
index 0000000..21b45b1
--- /dev/null
+++ b/test/rpythoninput/func_genexpr.py
@@ -0,0 +1,11 @@
+import os
+
+def function(l):
+ os.write(1, '%s\n' % ';'.join(str(v) for v in l))
+
+def entry_point(argv):
+ function([1, 2, 3, 4, 5, 6])
+ return 0
+
+def target(*args):
+ return entry_point, None
diff --git a/test/rpythoninput/func_immutable_global.py b/test/rpythoninput/func_immutable_global.py
new file mode 100644
index 0000000..33d3ba6
--- /dev/null
+++ b/test/rpythoninput/func_immutable_global.py
@@ -0,0 +1,15 @@
+GLOB = {}
+
+def function():
+ v = GLOB
+ v['key'] = "value"
+
+def entry_point(argv):
+ function()
+ if GLOB['key'] == 'value':
+ return 0
+ return 1
+
+def target(*args):
+ return entry_point, None
+
diff --git a/test/rpythoninput/func_immutable_global1.py b/test/rpythoninput/func_immutable_global1.py
new file mode 100644
index 0000000..4eace03
--- /dev/null
+++ b/test/rpythoninput/func_immutable_global1.py
@@ -0,0 +1,14 @@
+GLOB = []
+
+def function():
+ GLOB.append("value")
+
+def entry_point(argv):
+ function()
+ if GLOB['key'] == 'value':
+ return 0
+ return 1
+
+def target(*args):
+ return entry_point, None
+
diff --git a/test/rpythoninput/func_immutable_global2.py b/test/rpythoninput/func_immutable_global2.py
new file mode 100644
index 0000000..acb5415
--- /dev/null
+++ b/test/rpythoninput/func_immutable_global2.py
@@ -0,0 +1,15 @@
+GLOB = 1
+
+def function():
+ global GLOB
+ GLOB = "value"
+
+def entry_point(argv):
+ function()
+ if GLOB == 'value':
+ return 0
+ return 1
+
+def target(*args):
+ return entry_point, None
+
diff --git a/test/rpythoninput/func_immutable_global3.py b/test/rpythoninput/func_immutable_global3.py
new file mode 100644
index 0000000..fc2a51d
--- /dev/null
+++ b/test/rpythoninput/func_immutable_global3.py
@@ -0,0 +1,15 @@
+
+def function():
+ from rpythoninput import immutable_global3
+ immutable_global3.GLOB = 2 # was 1
+
+def entry_point(argv):
+ function()
+ from rpythoninput import immutable_global3
+ if immutable_global3.GLOB == 2:
+ return 0
+ return 1
+
+def target(*args):
+ return entry_point, None
+
diff --git a/test/rpythoninput/func_multiple_inheritance.py b/test/rpythoninput/func_multiple_inheritance.py
new file mode 100644
index 0000000..be798bf
--- /dev/null
+++ b/test/rpythoninput/func_multiple_inheritance.py
@@ -0,0 +1,20 @@
+class A:
+ pass
+
+class B:
+ pass
+
+class C(A, B):
+ def __init__(self, a):
+ self.attr = a
+
+
+def function(l):
+ os.write(1, '%s\n' % C(l))
+
+def entry_point(argv):
+ function(argv)
+ return 0
+
+def target(*args):
+ return entry_point, None
diff --git a/test/rpythoninput/func_multiple_inheritance2.py b/test/rpythoninput/func_multiple_inheritance2.py
new file mode 100644
index 0000000..4f62021
--- /dev/null
+++ b/test/rpythoninput/func_multiple_inheritance2.py
@@ -0,0 +1,22 @@
+class A:
+ pass
+
+class B:
+ _mixin_ = True
+ def __init__(self):
+ self.mixattr = None # not a pure mixin !
+
+class C(A, B):
+ def __init__(self, a):
+ self.attr = a
+
+
+def function(l):
+ os.write(1, '%s\n' % C(l))
+
+def entry_point(argv):
+ function(argv)
+ return 0
+
+def target(*args):
+ return entry_point, None
diff --git a/test/rpythoninput/func_multiple_types_assignment.py b/test/rpythoninput/func_multiple_types_assignment.py
new file mode 100644
index 0000000..a294552
--- /dev/null
+++ b/test/rpythoninput/func_multiple_types_assignment.py
@@ -0,0 +1,30 @@
+import os
+
+def function1(i):
+ attr = None
+ if i == 1:
+ attr = 1
+ else:
+ attr = "hello"
+ return attr
+
+def function2(i):
+ attr = None
+ if i == 1:
+ attr = 1
+ return attr
+
+def function_ok(i):
+ attr = None
+ if i == 1:
+ attr = "hello"
+ return attr
+
+def entry_point(argv):
+ os.write(str(function1(len(argv))))
+ os.write(str(function_ok(len(argv))))
+ return 0
+
+
+def target(*args):
+ return entry_point, None
diff --git a/test/rpythoninput/func_nobuiltin_buffer.py b/test/rpythoninput/func_nobuiltin_buffer.py
new file mode 100644
index 0000000..eda0846
--- /dev/null
+++ b/test/rpythoninput/func_nobuiltin_buffer.py
@@ -0,0 +1,13 @@
+
+import os
+
+def function():
+ retval = buffer()
+ os.write(2, str(retval) + '\n')
+
+def entry_point(argv):
+ function()
+ return 0
+
+def target(*args):
+ return entry_point, None
diff --git a/test/rpythoninput/func_nobuiltin_callable.py b/test/rpythoninput/func_nobuiltin_callable.py
new file mode 100644
index 0000000..830277f
--- /dev/null
+++ b/test/rpythoninput/func_nobuiltin_callable.py
@@ -0,0 +1,13 @@
+
+import os
+
+def function():
+ retval = callable(function)
+ os.write(2, str(retval) + '\n')
+
+def entry_point(argv):
+ function()
+ return 0
+
+def target(*args):
+ return entry_point, None
diff --git a/test/rpythoninput/func_nobuiltin_classmethod.py b/test/rpythoninput/func_nobuiltin_classmethod.py
new file mode 100644
index 0000000..bf2827b
--- /dev/null
+++ b/test/rpythoninput/func_nobuiltin_classmethod.py
@@ -0,0 +1,13 @@
+
+import os
+
+def function():
+ retval = classmethod(function)
+ os.write(2, str(retval) + '\n')
+
+def entry_point(argv):
+ function()
+ return 0
+
+def target(*args):
+ return entry_point, None
diff --git a/test/rpythoninput/func_nobuiltin_compile.py b/test/rpythoninput/func_nobuiltin_compile.py
new file mode 100644
index 0000000..f170dca
--- /dev/null
+++ b/test/rpythoninput/func_nobuiltin_compile.py
@@ -0,0 +1,13 @@
+
+import os
+
+def function():
+ retval = compile("a==b", "?", "eval")
+ os.write(2, str(retval) + '\n')
+
+def entry_point(argv):
+ function()
+ return 0
+
+def target(*args):
+ return entry_point, None
diff --git a/test/rpythoninput/func_nobuiltin_complex.py b/test/rpythoninput/func_nobuiltin_complex.py
new file mode 100644
index 0000000..186b584
--- /dev/null
+++ b/test/rpythoninput/func_nobuiltin_complex.py
@@ -0,0 +1,13 @@
+
+import os
+
+def function():
+ retval = complex(3, 4)
+ os.write(2, str(retval) + '\n')
+
+def entry_point(argv):
+ function()
+ return 0
+
+def target(*args):
+ return entry_point, None
diff --git a/test/rpythoninput/func_nobuiltin_delattr.py b/test/rpythoninput/func_nobuiltin_delattr.py
new file mode 100644
index 0000000..cbe7cde
--- /dev/null
+++ b/test/rpythoninput/func_nobuiltin_delattr.py
@@ -0,0 +1,13 @@
+
+import os
+
+def function():
+ retval = delattr(function, "bar")
+ os.write(2, str(retval) + '\n')
+
+def entry_point(argv):
+ function()
+ return 0
+
+def target(*args):
+ return entry_point, None
diff --git a/test/rpythoninput/func_nobuiltin_dict.py b/test/rpythoninput/func_nobuiltin_dict.py
new file mode 100644
index 0000000..c47ed20
--- /dev/null
+++ b/test/rpythoninput/func_nobuiltin_dict.py
@@ -0,0 +1,13 @@
+
+import os
+
+def function():
+ retval = dict()
+ os.write(2, str(retval) + '\n')
+
+def entry_point(argv):
+ function()
+ return 0
+
+def target(*args):
+ return entry_point, None
diff --git a/test/rpythoninput/func_nobuiltin_dir.py b/test/rpythoninput/func_nobuiltin_dir.py
new file mode 100644
index 0000000..fe048e1
--- /dev/null
+++ b/test/rpythoninput/func_nobuiltin_dir.py
@@ -0,0 +1,13 @@
+
+import os
+
+def function():
+ retval = dir()
+ os.write(2, str(retval) + '\n')
+
+def entry_point(argv):
+ function()
+ return 0
+
+def target(*args):
+ return entry_point, None
diff --git a/test/rpythoninput/func_nobuiltin_enumerate.py b/test/rpythoninput/func_nobuiltin_enumerate.py
new file mode 100644
index 0000000..dd59d12
--- /dev/null
+++ b/test/rpythoninput/func_nobuiltin_enumerate.py
@@ -0,0 +1,13 @@
+
+import os
+
+def function():
+ retval = enumerate(range(5))
+ os.write(2, str(retval) + '\n')
+
+def entry_point(argv):
+ function()
+ return 0
+
+def target(*args):
+ return entry_point, None
diff --git a/test/rpythoninput/func_nobuiltin_eval.py b/test/rpythoninput/func_nobuiltin_eval.py
new file mode 100644
index 0000000..a8a898c
--- /dev/null
+++ b/test/rpythoninput/func_nobuiltin_eval.py
@@ -0,0 +1,13 @@
+
+import os
+
+def function():
+ retval = eval("a == b")
+ os.write(2, str(retval) + '\n')
+
+def entry_point(argv):
+ function()
+ return 0
+
+def target(*args):
+ return entry_point, None
diff --git a/test/rpythoninput/func_nobuiltin_execfile.py b/test/rpythoninput/func_nobuiltin_execfile.py
new file mode 100644
index 0000000..7974bcd
--- /dev/null
+++ b/test/rpythoninput/func_nobuiltin_execfile.py
@@ -0,0 +1,13 @@
+
+import os
+
+def function():
+ retval = execfile("foo.py")
+ os.write(2, str(retval) + '\n')
+
+def entry_point(argv):
+ function()
+ return 0
+
+def target(*args):
+ return entry_point, None
diff --git a/test/rpythoninput/func_nobuiltin_file.py b/test/rpythoninput/func_nobuiltin_file.py
new file mode 100644
index 0000000..4484d90
--- /dev/null
+++ b/test/rpythoninput/func_nobuiltin_file.py
@@ -0,0 +1,13 @@
+
+import os
+
+def function():
+ retval = file("foo.txt")
+ os.write(2, str(retval) + '\n')
+
+def entry_point(argv):
+ function()
+ return 0
+
+def target(*args):
+ return entry_point, None
diff --git a/test/rpythoninput/func_nobuiltin_filter.py b/test/rpythoninput/func_nobuiltin_filter.py
new file mode 100644
index 0000000..2e49672
--- /dev/null
+++ b/test/rpythoninput/func_nobuiltin_filter.py
@@ -0,0 +1,13 @@
+
+import os
+
+def function():
+ retval = filter(function, [])
+ os.write(2, str(retval) + '\n')
+
+def entry_point(argv):
+ function()
+ return 0
+
+def target(*args):
+ return entry_point, None
diff --git a/test/rpythoninput/func_nobuiltin_frozenset.py b/test/rpythoninput/func_nobuiltin_frozenset.py
new file mode 100644
index 0000000..9440dd4
--- /dev/null
+++ b/test/rpythoninput/func_nobuiltin_frozenset.py
@@ -0,0 +1,13 @@
+
+import os
+
+def function():
+ retval = frozenset()
+ os.write(2, str(retval) + '\n')
+
+def entry_point(argv):
+ function()
+ return 0
+
+def target(*args):
+ return entry_point, None
diff --git a/test/rpythoninput/func_nobuiltin_getattr.py b/test/rpythoninput/func_nobuiltin_getattr.py
new file mode 100644
index 0000000..1a6da7b
--- /dev/null
+++ b/test/rpythoninput/func_nobuiltin_getattr.py
@@ -0,0 +1,13 @@
+
+import os
+
+def function():
+ retval = getattr(function, "bar")
+ os.write(2, str(retval) + '\n')
+
+def entry_point(argv):
+ function()
+ return 0
+
+def target(*args):
+ return entry_point, None
diff --git a/test/rpythoninput/func_nobuiltin_globals.py b/test/rpythoninput/func_nobuiltin_globals.py
new file mode 100644
index 0000000..409b3cd
--- /dev/null
+++ b/test/rpythoninput/func_nobuiltin_globals.py
@@ -0,0 +1,13 @@
+
+import os
+
+def function():
+ retval = globals()
+ os.write(2, str(retval) + '\n')
+
+def entry_point(argv):
+ function()
+ return 0
+
+def target(*args):
+ return entry_point, None
diff --git a/test/rpythoninput/func_nobuiltin_help.py b/test/rpythoninput/func_nobuiltin_help.py
new file mode 100644
index 0000000..48e54a1
--- /dev/null
+++ b/test/rpythoninput/func_nobuiltin_help.py
@@ -0,0 +1,13 @@
+
+import os
+
+def function():
+ retval = help(function)
+ os.write(2, str(retval) + '\n')
+
+def entry_point(argv):
+ function()
+ return 0
+
+def target(*args):
+ return entry_point, None
diff --git a/test/rpythoninput/func_nobuiltin_id.py b/test/rpythoninput/func_nobuiltin_id.py
new file mode 100644
index 0000000..a053fd5
--- /dev/null
+++ b/test/rpythoninput/func_nobuiltin_id.py
@@ -0,0 +1,13 @@
+
+import os
+
+def function():
+ retval = id(function)
+ os.write(2, str(retval) + '\n')
+
+def entry_point(argv):
+ function()
+ return 0
+
+def target(*args):
+ return entry_point, None
diff --git a/test/rpythoninput/func_nobuiltin_input.py b/test/rpythoninput/func_nobuiltin_input.py
new file mode 100644
index 0000000..42ab233
--- /dev/null
+++ b/test/rpythoninput/func_nobuiltin_input.py
@@ -0,0 +1,13 @@
+
+import os
+
+def function():
+ retval = input()
+ os.write(2, str(retval) + '\n')
+
+def entry_point(argv):
+ function()
+ return 0
+
+def target(*args):
+ return entry_point, None
diff --git a/test/rpythoninput/func_nobuiltin_intern.py b/test/rpythoninput/func_nobuiltin_intern.py
new file mode 100644
index 0000000..3e5b51e
--- /dev/null
+++ b/test/rpythoninput/func_nobuiltin_intern.py
@@ -0,0 +1,13 @@
+
+import os
+
+def function():
+ retval = intern("foo")
+ os.write(2, str(retval) + '\n')
+
+def entry_point(argv):
+ function()
+ return 0
+
+def target(*args):
+ return entry_point, None
diff --git a/test/rpythoninput/func_nobuiltin_issubclass.py b/test/rpythoninput/func_nobuiltin_issubclass.py
new file mode 100644
index 0000000..a0c5ca7
--- /dev/null
+++ b/test/rpythoninput/func_nobuiltin_issubclass.py
@@ -0,0 +1,13 @@
+
+import os
+
+def function():
+ retval = issubclass(function, str)
+ os.write(2, str(retval) + '\n')
+
+def entry_point(argv):
+ function()
+ return 0
+
+def target(*args):
+ return entry_point, None
diff --git a/test/rpythoninput/func_nobuiltin_iter.py b/test/rpythoninput/func_nobuiltin_iter.py
new file mode 100644
index 0000000..5a425b7
--- /dev/null
+++ b/test/rpythoninput/func_nobuiltin_iter.py
@@ -0,0 +1,13 @@
+
+import os
+
+def function():
+ retval = iter([])
+ os.write(2, str(retval) + '\n')
+
+def entry_point(argv):
+ function()
+ return 0
+
+def target(*args):
+ return entry_point, None
diff --git a/test/rpythoninput/func_nobuiltin_locals.py b/test/rpythoninput/func_nobuiltin_locals.py
new file mode 100644
index 0000000..c87ad4f
--- /dev/null
+++ b/test/rpythoninput/func_nobuiltin_locals.py
@@ -0,0 +1,13 @@
+
+import os
+
+def function():
+ retval = locals()
+ os.write(2, str(retval) + '\n')
+
+def entry_point(argv):
+ function()
+ return 0
+
+def target(*args):
+ return entry_point, None
diff --git a/test/rpythoninput/func_nobuiltin_map.py b/test/rpythoninput/func_nobuiltin_map.py
new file mode 100644
index 0000000..d2c42b7
--- /dev/null
+++ b/test/rpythoninput/func_nobuiltin_map.py
@@ -0,0 +1,13 @@
+
+import os
+
+def function():
+ retval = map(function, [])
+ os.write(2, str(retval) + '\n')
+
+def entry_point(argv):
+ function()
+ return 0
+
+def target(*args):
+ return entry_point, None
diff --git a/test/rpythoninput/func_nobuiltin_object.py b/test/rpythoninput/func_nobuiltin_object.py
new file mode 100644
index 0000000..3151700
--- /dev/null
+++ b/test/rpythoninput/func_nobuiltin_object.py
@@ -0,0 +1,13 @@
+
+import os
+
+def function():
+ retval = object()
+ os.write(2, str(retval) + '\n')
+
+def entry_point(argv):
+ function()
+ return 0
+
+def target(*args):
+ return entry_point, None
diff --git a/test/rpythoninput/func_nobuiltin_open.py b/test/rpythoninput/func_nobuiltin_open.py
new file mode 100644
index 0000000..b597f12
--- /dev/null
+++ b/test/rpythoninput/func_nobuiltin_open.py
@@ -0,0 +1,13 @@
+
+import os
+
+def function():
+ retval = open("foo.txt")
+ os.write(2, str(retval) + '\n')
+
+def entry_point(argv):
+ function()
+ return 0
+
+def target(*args):
+ return entry_point, None
diff --git a/test/rpythoninput/func_nobuiltin_property.py b/test/rpythoninput/func_nobuiltin_property.py
new file mode 100644
index 0000000..6724497
--- /dev/null
+++ b/test/rpythoninput/func_nobuiltin_property.py
@@ -0,0 +1,13 @@
+
+import os
+
+def function():
+ retval = property(function)
+ os.write(2, str(retval) + '\n')
+
+def entry_point(argv):
+ function()
+ return 0
+
+def target(*args):
+ return entry_point, None
diff --git a/test/rpythoninput/func_nobuiltin_raw_input.py b/test/rpythoninput/func_nobuiltin_raw_input.py
new file mode 100644
index 0000000..3b1f7e3
--- /dev/null
+++ b/test/rpythoninput/func_nobuiltin_raw_input.py
@@ -0,0 +1,13 @@
+
+import os
+
+def function():
+ retval = raw_input()
+ os.write(2, str(retval) + '\n')
+
+def entry_point(argv):
+ function()
+ return 0
+
+def target(*args):
+ return entry_point, None
diff --git a/test/rpythoninput/func_nobuiltin_reduce.py b/test/rpythoninput/func_nobuiltin_reduce.py
new file mode 100644
index 0000000..c314fd1
--- /dev/null
+++ b/test/rpythoninput/func_nobuiltin_reduce.py
@@ -0,0 +1,13 @@
+
+import os
+
+def function():
+ retval = reduce(function, [])
+ os.write(2, str(retval) + '\n')
+
+def entry_point(argv):
+ function()
+ return 0
+
+def target(*args):
+ return entry_point, None
diff --git a/test/rpythoninput/func_nobuiltin_reload.py b/test/rpythoninput/func_nobuiltin_reload.py
new file mode 100644
index 0000000..544d5ab
--- /dev/null
+++ b/test/rpythoninput/func_nobuiltin_reload.py
@@ -0,0 +1,13 @@
+
+import os
+
+def function():
+ retval = reload(module)
+ os.write(2, str(retval) + '\n')
+
+def entry_point(argv):
+ function()
+ return 0
+
+def target(*args):
+ return entry_point, None
diff --git a/test/rpythoninput/func_nobuiltin_reversed.py b/test/rpythoninput/func_nobuiltin_reversed.py
new file mode 100644
index 0000000..4937c94
--- /dev/null
+++ b/test/rpythoninput/func_nobuiltin_reversed.py
@@ -0,0 +1,13 @@
+
+import os
+
+def function():
+ retval = reversed(range(5))
+ os.write(2, str(retval) + '\n')
+
+def entry_point(argv):
+ function()
+ return 0
+
+def target(*args):
+ return entry_point, None
diff --git a/test/rpythoninput/func_nobuiltin_round.py b/test/rpythoninput/func_nobuiltin_round.py
new file mode 100644
index 0000000..530f34e
--- /dev/null
+++ b/test/rpythoninput/func_nobuiltin_round.py
@@ -0,0 +1,13 @@
+
+import os
+
+def function():
+ retval = round(3.4)
+ os.write(2, str(retval) + '\n')
+
+def entry_point(argv):
+ function()
+ return 0
+
+def target(*args):
+ return entry_point, None
diff --git a/test/rpythoninput/func_nobuiltin_set.py b/test/rpythoninput/func_nobuiltin_set.py
new file mode 100644
index 0000000..5de5c1d
--- /dev/null
+++ b/test/rpythoninput/func_nobuiltin_set.py
@@ -0,0 +1,13 @@
+
+import os
+
+def function():
+ retval = set([1, 2, 3])
+ os.write(2, str(retval) + '\n')
+
+def entry_point(argv):
+ function()
+ return 0
+
+def target(*args):
+ return entry_point, None
diff --git a/test/rpythoninput/func_nobuiltin_setattr.py b/test/rpythoninput/func_nobuiltin_setattr.py
new file mode 100644
index 0000000..27eb472
--- /dev/null
+++ b/test/rpythoninput/func_nobuiltin_setattr.py
@@ -0,0 +1,13 @@
+
+import os
+
+def function():
+ retval = setattr(function, "bar", "baz")
+ os.write(2, str(retval) + '\n')
+
+def entry_point(argv):
+ function()
+ return 0
+
+def target(*args):
+ return entry_point, None
diff --git a/test/rpythoninput/func_nobuiltin_sorted.py b/test/rpythoninput/func_nobuiltin_sorted.py
new file mode 100644
index 0000000..ab495c0
--- /dev/null
+++ b/test/rpythoninput/func_nobuiltin_sorted.py
@@ -0,0 +1,13 @@
+
+import os
+
+def function():
+ retval = sorted(range(5))
+ os.write(2, str(retval) + '\n')
+
+def entry_point(argv):
+ function()
+ return 0
+
+def target(*args):
+ return entry_point, None
diff --git a/test/rpythoninput/func_nobuiltin_staticmethod.py b/test/rpythoninput/func_nobuiltin_staticmethod.py
new file mode 100644
index 0000000..888471c
--- /dev/null
+++ b/test/rpythoninput/func_nobuiltin_staticmethod.py
@@ -0,0 +1,13 @@
+
+import os
+
+def function():
+ retval = staticmethod(function)
+ os.write(2, str(retval) + '\n')
+
+def entry_point(argv):
+ function()
+ return 0
+
+def target(*args):
+ return entry_point, None
diff --git a/test/rpythoninput/func_nobuiltin_sum.py b/test/rpythoninput/func_nobuiltin_sum.py
new file mode 100644
index 0000000..8e33ed5
--- /dev/null
+++ b/test/rpythoninput/func_nobuiltin_sum.py
@@ -0,0 +1,13 @@
+
+import os
+
+def function():
+ retval = sum(xrange(4))
+ os.write(2, str(retval) + '\n')
+
+def entry_point(argv):
+ function()
+ return 0
+
+def target(*args):
+ return entry_point, None
diff --git a/test/rpythoninput/func_nobuiltin_super.py b/test/rpythoninput/func_nobuiltin_super.py
new file mode 100644
index 0000000..cc9106a
--- /dev/null
+++ b/test/rpythoninput/func_nobuiltin_super.py
@@ -0,0 +1,13 @@
+
+import os
+
+def function():
+ retval = super(str)
+ os.write(2, str(retval) + '\n')
+
+def entry_point(argv):
+ function()
+ return 0
+
+def target(*args):
+ return entry_point, None
diff --git a/test/rpythoninput/func_nobuiltin_unicode.py b/test/rpythoninput/func_nobuiltin_unicode.py
new file mode 100644
index 0000000..c3e1107
--- /dev/null
+++ b/test/rpythoninput/func_nobuiltin_unicode.py
@@ -0,0 +1,13 @@
+
+import os
+
+def function():
+ retval = unicode("f")
+ os.write(2, str(retval) + '\n')
+
+def entry_point(argv):
+ function()
+ return 0
+
+def target(*args):
+ return entry_point, None
diff --git a/test/rpythoninput/func_nobuiltin_vars.py b/test/rpythoninput/func_nobuiltin_vars.py
new file mode 100644
index 0000000..19e3e9d
--- /dev/null
+++ b/test/rpythoninput/func_nobuiltin_vars.py
@@ -0,0 +1,13 @@
+
+import os
+
+def function():
+ retval = vars()
+ os.write(2, str(retval) + '\n')
+
+def entry_point(argv):
+ function()
+ return 0
+
+def target(*args):
+ return entry_point, None
diff --git a/test/rpythoninput/func_noerror_multiple_inheritance.py b/test/rpythoninput/func_noerror_multiple_inheritance.py
new file mode 100644
index 0000000..050b2c3
--- /dev/null
+++ b/test/rpythoninput/func_noerror_multiple_inheritance.py
@@ -0,0 +1,25 @@
+class A:
+ def __init__(self):
+ self.parentattr = None
+
+class B:
+ _mixin_ = True
+
+class C(A, B):
+ def __init__(self, a):
+ A.__init__(self)
+ self.attr = a
+
+
+def function(l):
+ os.write(1, '%s\n' % C(l))
+
+def entry_point(argv):
+ function(argv)
+ return 0
+
+def target(*args):
+ return entry_point, None
+
+import os
+
diff --git a/test/rpythoninput/func_noerror_mutable_global.py b/test/rpythoninput/func_noerror_mutable_global.py
new file mode 100644
index 0000000..24ccb65
--- /dev/null
+++ b/test/rpythoninput/func_noerror_mutable_global.py
@@ -0,0 +1,18 @@
+class Foo:
+ def __init__(self):
+ self.bar = None
+
+X = Foo()
+
+def function():
+ X.bar = 'quux'
+
+
+def entry_point(argv):
+ function()
+ if X.bar == 'quux':
+ return 0
+ return 1
+
+def target(*args):
+ return entry_point, None
diff --git a/test/rpythoninput/func_noerror_notrpython.py b/test/rpythoninput/func_noerror_notrpython.py
new file mode 100644
index 0000000..9b756c7
--- /dev/null
+++ b/test/rpythoninput/func_noerror_notrpython.py
@@ -0,0 +1,12 @@
+def function(attrname):
+ """NOT RPYTHON"""
+ # I'm free ...
+ yield getattr(unicode, attrname)
+ yield getattr(str, attrname)
+
+def entry_point(argv):
+ return 0
+
+
+def target(*args):
+ return entry_point, None
diff --git a/test/rpythoninput/func_noerror_slice_index.py b/test/rpythoninput/func_noerror_slice_index.py
new file mode 100644
index 0000000..2dfe988
--- /dev/null
+++ b/test/rpythoninput/func_noerror_slice_index.py
@@ -0,0 +1,17 @@
+import os
+
+def function(l):
+ os.write(1, '%s\n' % l[:-1])
+ os.write(1, '%s\n' % l[0:-1])
+ os.write(1, '%s\n' % l[:-1:])
+ os.write(1, '%s\n' % l[0:2])
+ step = 1
+ os.write(1, '%s\n' % l[0:2:step])
+
+
+def entry_point(argv):
+ function([1, 2, 3, 4, 5, 6])
+ return 0
+
+def target(*args):
+ return entry_point, None
diff --git a/test/rpythoninput/func_non_homogeneous_list.py b/test/rpythoninput/func_non_homogeneous_list.py
new file mode 100644
index 0000000..a0d09db
--- /dev/null
+++ b/test/rpythoninput/func_non_homogeneous_list.py
@@ -0,0 +1,12 @@
+GLOB = 1
+
+def function(i):
+ return [i, GLOB, "hop"]
+
+def entry_point(argv):
+ function(len(argv))
+ return 0
+
+
+def target(*args):
+ return entry_point, None
diff --git a/test/rpythoninput/func_noyield.py b/test/rpythoninput/func_noyield.py
new file mode 100644
index 0000000..72f8537
--- /dev/null
+++ b/test/rpythoninput/func_noyield.py
@@ -0,0 +1,17 @@
+import os
+
+def function():
+ yield "hello"
+ yield "hello"
+ yield "hello"
+
+def entry_point(argv):
+ count = 0
+ for elt in function():
+ count += 1
+ if count == 3:
+ return 0 # OK
+ return 1 # ERROR
+
+def target(*args):
+ return entry_point, None
diff --git a/test/rpythoninput/func_repr_format_string.py b/test/rpythoninput/func_repr_format_string.py
new file mode 100644
index 0000000..4a8c062
--- /dev/null
+++ b/test/rpythoninput/func_repr_format_string.py
@@ -0,0 +1,12 @@
+import os
+
+def function(l):
+ os.write(1, '%r\n' % l[-1])
+ os.write(1, '%(list)r\n' % {'list':l})
+
+def entry_point(argv):
+ function([1, 2, 3, 4, 5, 6])
+ return 0
+
+def target(*args):
+ return entry_point, None
diff --git a/test/rpythoninput/func_slice_negative_index.py b/test/rpythoninput/func_slice_negative_index.py
new file mode 100644
index 0000000..cd247ab
--- /dev/null
+++ b/test/rpythoninput/func_slice_negative_index.py
@@ -0,0 +1,20 @@
+import os
+
+A = -1
+B = -2
+
+def function(l, step=1):
+ os.write(1, '%s\n' % l[-1:])
+ os.write(1, '%s\n' % l[1:-1])
+ os.write(1, '%s\n' % l[A:B])
+ os.write(1, '%s\n' % l[-1:B:A])
+ os.write(1, '%s\n' % l[-1::])
+ os.write(1, '%s\n' % l[::-1])
+
+
+def entry_point(argv):
+ function([1, 2, 3, 4, 5, 6])
+ return 0
+
+def target(*args):
+ return entry_point, None
diff --git a/test/rpythoninput/func_slice_non_constant_step.py b/test/rpythoninput/func_slice_non_constant_step.py
new file mode 100644
index 0000000..b6eafac
--- /dev/null
+++ b/test/rpythoninput/func_slice_non_constant_step.py
@@ -0,0 +1,12 @@
+import os
+
+def function(l, step=1):
+ os.write(1, '%s\n' % l[1:3:step])
+
+
+def entry_point(argv):
+ function([1, 2, 3, 4, 5, 6], len(argv))
+ return 0
+
+def target(*args):
+ return entry_point, None
diff --git a/test/rpythoninput/func_unsupported_protocol.py b/test/rpythoninput/func_unsupported_protocol.py
new file mode 100644
index 0000000..427d5da
--- /dev/null
+++ b/test/rpythoninput/func_unsupported_protocol.py
@@ -0,0 +1,23 @@
+# pylint: disable-msg=E1202
+import os
+
+class C(object):
+ def __new__(cls, *args):
+ os.write('new!')
+ return object.__new__(cls, *args)
+
+ def __init__(self, a):
+ self.attr = a
+
+ def __add__(self, other):
+ return self.attr + other
+
+def function(l):
+ os.write(1, '%s\n' % (C(len(l)) + 4))
+
+def entry_point(argv):
+ function(argv)
+ return 0
+
+def target(*args):
+ return entry_point, None
diff --git a/test/rpythoninput/immutable_global3.py b/test/rpythoninput/immutable_global3.py
new file mode 100644
index 0000000..311eec1
--- /dev/null
+++ b/test/rpythoninput/immutable_global3.py
@@ -0,0 +1 @@
+GLOB = 1
diff --git a/test/rpythonmessages/func_genexpr.txt b/test/rpythonmessages/func_genexpr.txt
new file mode 100644
index 0000000..0b64c6f
--- /dev/null
+++ b/test/rpythonmessages/func_genexpr.txt
@@ -0,0 +1 @@
+E: 1:function: generator expressions are not supported
diff --git a/test/rpythonmessages/func_immutable_global.txt b/test/rpythonmessages/func_immutable_global.txt
new file mode 100644
index 0000000..2cf59c6
--- /dev/null
+++ b/test/rpythonmessages/func_immutable_global.txt
@@ -0,0 +1 @@
+E: 5:function: Modifying global 'v' from module rpythoninput.func_immutable_global
diff --git a/test/rpythonmessages/func_immutable_global1.txt b/test/rpythonmessages/func_immutable_global1.txt
new file mode 100644
index 0000000..ed9c39e
--- /dev/null
+++ b/test/rpythonmessages/func_immutable_global1.txt
@@ -0,0 +1 @@
+E: 4:function: Modifying global 'GLOB' from module rpythoninput.func_immutable_global1
diff --git a/test/rpythonmessages/func_immutable_global2.txt b/test/rpythonmessages/func_immutable_global2.txt
new file mode 100644
index 0000000..3e9578a
--- /dev/null
+++ b/test/rpythonmessages/func_immutable_global2.txt
@@ -0,0 +1 @@
+E: 5:function: Modifying global 'GLOB' from module rpythoninput.func_immutable_global2
diff --git a/test/rpythonmessages/func_immutable_global3.txt b/test/rpythonmessages/func_immutable_global3.txt
new file mode 100644
index 0000000..1d43f23
--- /dev/null
+++ b/test/rpythonmessages/func_immutable_global3.txt
@@ -0,0 +1,2 @@
+E: 4:function: Modifying global 'GLOB' from module rpythoninput.immutable_global3
+
diff --git a/test/rpythonmessages/func_multiple_inheritance.txt b/test/rpythonmessages/func_multiple_inheritance.txt
new file mode 100644
index 0000000..4aba34f
--- /dev/null
+++ b/test/rpythonmessages/func_multiple_inheritance.txt
@@ -0,0 +1 @@
+E: 7:C: multiple inheritance only supported under specific rules which doesn't seems to be satisfied
diff --git a/test/rpythonmessages/func_multiple_inheritance2.txt b/test/rpythonmessages/func_multiple_inheritance2.txt
new file mode 100644
index 0000000..41e8a8a
--- /dev/null
+++ b/test/rpythonmessages/func_multiple_inheritance2.txt
@@ -0,0 +1 @@
+E: 9:C: multiple inheritance only supported under specific rules which doesn't seems to be satisfied
diff --git a/test/rpythonmessages/func_multiple_types_assignment.txt b/test/rpythonmessages/func_multiple_types_assignment.txt
new file mode 100644
index 0000000..a2e181d
--- /dev/null
+++ b/test/rpythonmessages/func_multiple_types_assignment.txt
@@ -0,0 +1,2 @@
+E: 3:function1: Multiple types assigned to identifier 'attr'
+E: 11:function2: Can't mix int and None on identifier 'attr'
diff --git a/test/rpythonmessages/func_nobuiltin_buffer.txt b/test/rpythonmessages/func_nobuiltin_buffer.txt
new file mode 100644
index 0000000..cad00a4
--- /dev/null
+++ b/test/rpythonmessages/func_nobuiltin_buffer.txt
@@ -0,0 +1 @@
+E: 5:function: Using unavailable builtin 'buffer' \ No newline at end of file
diff --git a/test/rpythonmessages/func_nobuiltin_callable.txt b/test/rpythonmessages/func_nobuiltin_callable.txt
new file mode 100644
index 0000000..b136693
--- /dev/null
+++ b/test/rpythonmessages/func_nobuiltin_callable.txt
@@ -0,0 +1 @@
+E: 5:function: Using unavailable builtin 'callable' \ No newline at end of file
diff --git a/test/rpythonmessages/func_nobuiltin_classmethod.txt b/test/rpythonmessages/func_nobuiltin_classmethod.txt
new file mode 100644
index 0000000..34dd1ab
--- /dev/null
+++ b/test/rpythonmessages/func_nobuiltin_classmethod.txt
@@ -0,0 +1 @@
+E: 5:function: Using unavailable builtin 'classmethod' \ No newline at end of file
diff --git a/test/rpythonmessages/func_nobuiltin_compile.txt b/test/rpythonmessages/func_nobuiltin_compile.txt
new file mode 100644
index 0000000..806ed01
--- /dev/null
+++ b/test/rpythonmessages/func_nobuiltin_compile.txt
@@ -0,0 +1 @@
+E: 5:function: Using unavailable builtin 'compile' \ No newline at end of file
diff --git a/test/rpythonmessages/func_nobuiltin_complex.txt b/test/rpythonmessages/func_nobuiltin_complex.txt
new file mode 100644
index 0000000..cfd44af
--- /dev/null
+++ b/test/rpythonmessages/func_nobuiltin_complex.txt
@@ -0,0 +1 @@
+E: 5:function: Using unavailable builtin 'complex' \ No newline at end of file
diff --git a/test/rpythonmessages/func_nobuiltin_delattr.txt b/test/rpythonmessages/func_nobuiltin_delattr.txt
new file mode 100644
index 0000000..d75a246
--- /dev/null
+++ b/test/rpythonmessages/func_nobuiltin_delattr.txt
@@ -0,0 +1 @@
+E: 5:function: Using unavailable builtin 'delattr' \ No newline at end of file
diff --git a/test/rpythonmessages/func_nobuiltin_dict.txt b/test/rpythonmessages/func_nobuiltin_dict.txt
new file mode 100644
index 0000000..8926654
--- /dev/null
+++ b/test/rpythonmessages/func_nobuiltin_dict.txt
@@ -0,0 +1 @@
+E: 5:function: Using unavailable builtin 'dict' \ No newline at end of file
diff --git a/test/rpythonmessages/func_nobuiltin_dir.txt b/test/rpythonmessages/func_nobuiltin_dir.txt
new file mode 100644
index 0000000..484c26c
--- /dev/null
+++ b/test/rpythonmessages/func_nobuiltin_dir.txt
@@ -0,0 +1 @@
+E: 5:function: Using unavailable builtin 'dir' \ No newline at end of file
diff --git a/test/rpythonmessages/func_nobuiltin_enumerate.txt b/test/rpythonmessages/func_nobuiltin_enumerate.txt
new file mode 100644
index 0000000..4e8eabb
--- /dev/null
+++ b/test/rpythonmessages/func_nobuiltin_enumerate.txt
@@ -0,0 +1 @@
+E: 5:function: Using unavailable builtin 'enumerate' \ No newline at end of file
diff --git a/test/rpythonmessages/func_nobuiltin_eval.txt b/test/rpythonmessages/func_nobuiltin_eval.txt
new file mode 100644
index 0000000..ce5b6a4
--- /dev/null
+++ b/test/rpythonmessages/func_nobuiltin_eval.txt
@@ -0,0 +1 @@
+E: 5:function: Using unavailable builtin 'eval' \ No newline at end of file
diff --git a/test/rpythonmessages/func_nobuiltin_execfile.txt b/test/rpythonmessages/func_nobuiltin_execfile.txt
new file mode 100644
index 0000000..9e4fe7c
--- /dev/null
+++ b/test/rpythonmessages/func_nobuiltin_execfile.txt
@@ -0,0 +1 @@
+E: 5:function: Using unavailable builtin 'execfile' \ No newline at end of file
diff --git a/test/rpythonmessages/func_nobuiltin_file.txt b/test/rpythonmessages/func_nobuiltin_file.txt
new file mode 100644
index 0000000..8de81b5
--- /dev/null
+++ b/test/rpythonmessages/func_nobuiltin_file.txt
@@ -0,0 +1 @@
+E: 5:function: Using unavailable builtin 'file' \ No newline at end of file
diff --git a/test/rpythonmessages/func_nobuiltin_filter.txt b/test/rpythonmessages/func_nobuiltin_filter.txt
new file mode 100644
index 0000000..454b8d4
--- /dev/null
+++ b/test/rpythonmessages/func_nobuiltin_filter.txt
@@ -0,0 +1 @@
+E: 5:function: Using unavailable builtin 'filter' \ No newline at end of file
diff --git a/test/rpythonmessages/func_nobuiltin_frozenset.txt b/test/rpythonmessages/func_nobuiltin_frozenset.txt
new file mode 100644
index 0000000..5853873
--- /dev/null
+++ b/test/rpythonmessages/func_nobuiltin_frozenset.txt
@@ -0,0 +1 @@
+E: 5:function: Using unavailable builtin 'frozenset' \ No newline at end of file
diff --git a/test/rpythonmessages/func_nobuiltin_getattr.txt b/test/rpythonmessages/func_nobuiltin_getattr.txt
new file mode 100644
index 0000000..853698a
--- /dev/null
+++ b/test/rpythonmessages/func_nobuiltin_getattr.txt
@@ -0,0 +1 @@
+E: 5:function: Using unavailable builtin 'getattr' \ No newline at end of file
diff --git a/test/rpythonmessages/func_nobuiltin_globals.txt b/test/rpythonmessages/func_nobuiltin_globals.txt
new file mode 100644
index 0000000..d92ef44
--- /dev/null
+++ b/test/rpythonmessages/func_nobuiltin_globals.txt
@@ -0,0 +1 @@
+E: 5:function: Using unavailable builtin 'globals' \ No newline at end of file
diff --git a/test/rpythonmessages/func_nobuiltin_help.txt b/test/rpythonmessages/func_nobuiltin_help.txt
new file mode 100644
index 0000000..50d5a8b
--- /dev/null
+++ b/test/rpythonmessages/func_nobuiltin_help.txt
@@ -0,0 +1 @@
+E: 5:function: Using unavailable builtin 'help' \ No newline at end of file
diff --git a/test/rpythonmessages/func_nobuiltin_id.txt b/test/rpythonmessages/func_nobuiltin_id.txt
new file mode 100644
index 0000000..44b1044
--- /dev/null
+++ b/test/rpythonmessages/func_nobuiltin_id.txt
@@ -0,0 +1 @@
+E: 5:function: Using unavailable builtin 'id' \ No newline at end of file
diff --git a/test/rpythonmessages/func_nobuiltin_input.txt b/test/rpythonmessages/func_nobuiltin_input.txt
new file mode 100644
index 0000000..8df0e0c
--- /dev/null
+++ b/test/rpythonmessages/func_nobuiltin_input.txt
@@ -0,0 +1 @@
+E: 5:function: Using unavailable builtin 'input' \ No newline at end of file
diff --git a/test/rpythonmessages/func_nobuiltin_intern.txt b/test/rpythonmessages/func_nobuiltin_intern.txt
new file mode 100644
index 0000000..381113d
--- /dev/null
+++ b/test/rpythonmessages/func_nobuiltin_intern.txt
@@ -0,0 +1 @@
+E: 5:function: Using unavailable builtin 'intern' \ No newline at end of file
diff --git a/test/rpythonmessages/func_nobuiltin_issubclass.txt b/test/rpythonmessages/func_nobuiltin_issubclass.txt
new file mode 100644
index 0000000..72b8851
--- /dev/null
+++ b/test/rpythonmessages/func_nobuiltin_issubclass.txt
@@ -0,0 +1 @@
+E: 5:function: Using unavailable builtin 'issubclass' \ No newline at end of file
diff --git a/test/rpythonmessages/func_nobuiltin_iter.txt b/test/rpythonmessages/func_nobuiltin_iter.txt
new file mode 100644
index 0000000..ec4501e
--- /dev/null
+++ b/test/rpythonmessages/func_nobuiltin_iter.txt
@@ -0,0 +1 @@
+E: 5:function: Using unavailable builtin 'iter' \ No newline at end of file
diff --git a/test/rpythonmessages/func_nobuiltin_locals.txt b/test/rpythonmessages/func_nobuiltin_locals.txt
new file mode 100644
index 0000000..ff8fb73
--- /dev/null
+++ b/test/rpythonmessages/func_nobuiltin_locals.txt
@@ -0,0 +1 @@
+E: 5:function: Using unavailable builtin 'locals' \ No newline at end of file
diff --git a/test/rpythonmessages/func_nobuiltin_map.txt b/test/rpythonmessages/func_nobuiltin_map.txt
new file mode 100644
index 0000000..838a820
--- /dev/null
+++ b/test/rpythonmessages/func_nobuiltin_map.txt
@@ -0,0 +1 @@
+E: 5:function: Using unavailable builtin 'map' \ No newline at end of file
diff --git a/test/rpythonmessages/func_nobuiltin_object.txt b/test/rpythonmessages/func_nobuiltin_object.txt
new file mode 100644
index 0000000..667bfe6
--- /dev/null
+++ b/test/rpythonmessages/func_nobuiltin_object.txt
@@ -0,0 +1 @@
+E: 5:function: Using unavailable builtin 'object' \ No newline at end of file
diff --git a/test/rpythonmessages/func_nobuiltin_open.txt b/test/rpythonmessages/func_nobuiltin_open.txt
new file mode 100644
index 0000000..09e0a8c
--- /dev/null
+++ b/test/rpythonmessages/func_nobuiltin_open.txt
@@ -0,0 +1 @@
+E: 5:function: Using unavailable builtin 'open' \ No newline at end of file
diff --git a/test/rpythonmessages/func_nobuiltin_property.txt b/test/rpythonmessages/func_nobuiltin_property.txt
new file mode 100644
index 0000000..2c31d4a
--- /dev/null
+++ b/test/rpythonmessages/func_nobuiltin_property.txt
@@ -0,0 +1 @@
+E: 5:function: Using unavailable builtin 'property' \ No newline at end of file
diff --git a/test/rpythonmessages/func_nobuiltin_raw_input.txt b/test/rpythonmessages/func_nobuiltin_raw_input.txt
new file mode 100644
index 0000000..9c725d9
--- /dev/null
+++ b/test/rpythonmessages/func_nobuiltin_raw_input.txt
@@ -0,0 +1 @@
+E: 5:function: Using unavailable builtin 'raw_input' \ No newline at end of file
diff --git a/test/rpythonmessages/func_nobuiltin_reduce.txt b/test/rpythonmessages/func_nobuiltin_reduce.txt
new file mode 100644
index 0000000..b1c61cd
--- /dev/null
+++ b/test/rpythonmessages/func_nobuiltin_reduce.txt
@@ -0,0 +1 @@
+E: 5:function: Using unavailable builtin 'reduce' \ No newline at end of file
diff --git a/test/rpythonmessages/func_nobuiltin_reload.txt b/test/rpythonmessages/func_nobuiltin_reload.txt
new file mode 100644
index 0000000..2b96613
--- /dev/null
+++ b/test/rpythonmessages/func_nobuiltin_reload.txt
@@ -0,0 +1 @@
+E: 5:function: Using unavailable builtin 'reload' \ No newline at end of file
diff --git a/test/rpythonmessages/func_nobuiltin_reversed.txt b/test/rpythonmessages/func_nobuiltin_reversed.txt
new file mode 100644
index 0000000..8ae4a20
--- /dev/null
+++ b/test/rpythonmessages/func_nobuiltin_reversed.txt
@@ -0,0 +1 @@
+E: 5:function: Using unavailable builtin 'reversed' \ No newline at end of file
diff --git a/test/rpythonmessages/func_nobuiltin_round.txt b/test/rpythonmessages/func_nobuiltin_round.txt
new file mode 100644
index 0000000..44dde14
--- /dev/null
+++ b/test/rpythonmessages/func_nobuiltin_round.txt
@@ -0,0 +1 @@
+E: 5:function: Using unavailable builtin 'round' \ No newline at end of file
diff --git a/test/rpythonmessages/func_nobuiltin_set.txt b/test/rpythonmessages/func_nobuiltin_set.txt
new file mode 100644
index 0000000..2be0dab
--- /dev/null
+++ b/test/rpythonmessages/func_nobuiltin_set.txt
@@ -0,0 +1 @@
+E: 5:function: Using unavailable builtin 'set' \ No newline at end of file
diff --git a/test/rpythonmessages/func_nobuiltin_setattr.txt b/test/rpythonmessages/func_nobuiltin_setattr.txt
new file mode 100644
index 0000000..465d8a1
--- /dev/null
+++ b/test/rpythonmessages/func_nobuiltin_setattr.txt
@@ -0,0 +1 @@
+E: 5:function: Using unavailable builtin 'setattr' \ No newline at end of file
diff --git a/test/rpythonmessages/func_nobuiltin_sorted.txt b/test/rpythonmessages/func_nobuiltin_sorted.txt
new file mode 100644
index 0000000..94fa187
--- /dev/null
+++ b/test/rpythonmessages/func_nobuiltin_sorted.txt
@@ -0,0 +1 @@
+E: 5:function: Using unavailable builtin 'sorted' \ No newline at end of file
diff --git a/test/rpythonmessages/func_nobuiltin_staticmethod.txt b/test/rpythonmessages/func_nobuiltin_staticmethod.txt
new file mode 100644
index 0000000..93c0297
--- /dev/null
+++ b/test/rpythonmessages/func_nobuiltin_staticmethod.txt
@@ -0,0 +1 @@
+E: 5:function: Using unavailable builtin 'staticmethod' \ No newline at end of file
diff --git a/test/rpythonmessages/func_nobuiltin_sum.txt b/test/rpythonmessages/func_nobuiltin_sum.txt
new file mode 100644
index 0000000..373910f
--- /dev/null
+++ b/test/rpythonmessages/func_nobuiltin_sum.txt
@@ -0,0 +1 @@
+E: 5:function: Using unavailable builtin 'sum' \ No newline at end of file
diff --git a/test/rpythonmessages/func_nobuiltin_super.txt b/test/rpythonmessages/func_nobuiltin_super.txt
new file mode 100644
index 0000000..9c497b4
--- /dev/null
+++ b/test/rpythonmessages/func_nobuiltin_super.txt
@@ -0,0 +1 @@
+E: 5:function: Using unavailable builtin 'super' \ No newline at end of file
diff --git a/test/rpythonmessages/func_nobuiltin_unicode.txt b/test/rpythonmessages/func_nobuiltin_unicode.txt
new file mode 100644
index 0000000..68caf5e
--- /dev/null
+++ b/test/rpythonmessages/func_nobuiltin_unicode.txt
@@ -0,0 +1 @@
+E: 5:function: Using unavailable builtin 'unicode' \ No newline at end of file
diff --git a/test/rpythonmessages/func_nobuiltin_vars.txt b/test/rpythonmessages/func_nobuiltin_vars.txt
new file mode 100644
index 0000000..ae2f3c9
--- /dev/null
+++ b/test/rpythonmessages/func_nobuiltin_vars.txt
@@ -0,0 +1 @@
+E: 5:function: Using unavailable builtin 'vars' \ No newline at end of file
diff --git a/test/rpythonmessages/func_non_homogeneous_list.txt b/test/rpythonmessages/func_non_homogeneous_list.txt
new file mode 100644
index 0000000..10404bc
--- /dev/null
+++ b/test/rpythonmessages/func_non_homogeneous_list.txt
@@ -0,0 +1 @@
+E: 4:function: Non homogeneous values in list
diff --git a/test/rpythonmessages/func_noyield.txt b/test/rpythonmessages/func_noyield.txt
new file mode 100644
index 0000000..b0ac4b1
--- /dev/null
+++ b/test/rpythonmessages/func_noyield.txt
@@ -0,0 +1,3 @@
+E: 4:function: Using unavailable keyword 'yield'
+E: 5:function: Using unavailable keyword 'yield'
+E: 6:function: Using unavailable keyword 'yield'
diff --git a/test/rpythonmessages/func_repr_format_string.txt b/test/rpythonmessages/func_repr_format_string.txt
new file mode 100644
index 0000000..65ba565
--- /dev/null
+++ b/test/rpythonmessages/func_repr_format_string.txt
@@ -0,0 +1,2 @@
+E: 4:function: %r format is not supported
+E: 5:function: %r format is not supported
diff --git a/test/rpythonmessages/func_slice_negative_index.txt b/test/rpythonmessages/func_slice_negative_index.txt
new file mode 100644
index 0000000..2074112
--- /dev/null
+++ b/test/rpythonmessages/func_slice_negative_index.txt
@@ -0,0 +1,9 @@
+E: 7:function: Using negative slice start index -1 (infered to -1)
+E: 8:function: Using negative slice stop index -1 (infered to -1)
+E: 9:function: Using negative slice start index A (infered to -1)
+E: 9:function: Using negative slice stop index B (infered to -2)
+E: 10:function: Using negative slice start index -1 (infered to -1)
+E: 10:function: Using negative slice step A (infered to -1)
+E: 10:function: Using negative slice stop index B (infered to -2)
+E: 11:function: Using negative slice start index -1 (infered to -1)
+E: 12:function: Using negative slice step -1 (infered to -1)
diff --git a/test/rpythonmessages/func_slice_non_constant_step.txt b/test/rpythonmessages/func_slice_non_constant_step.txt
new file mode 100644
index 0000000..e537176
--- /dev/null
+++ b/test/rpythonmessages/func_slice_non_constant_step.txt
@@ -0,0 +1 @@
+E: 4:function: Using non constant step
diff --git a/test/rpythonmessages/func_unsupported_protocol.txt b/test/rpythonmessages/func_unsupported_protocol.txt
new file mode 100644
index 0000000..330be4f
--- /dev/null
+++ b/test/rpythonmessages/func_unsupported_protocol.txt
@@ -0,0 +1,2 @@
+E: 5:C.__new__: Using unavailable protocol '__new__'
+W: 12:C.__add__: special method __add__ has to be called explicitly
diff --git a/test/runtests.py b/test/runtests.py
new file mode 100644
index 0000000..67d6636
--- /dev/null
+++ b/test/runtests.py
@@ -0,0 +1,5 @@
+from logilab.common.testlib import main
+
+if __name__ == '__main__':
+ import sys, os
+ main(os.path.dirname(sys.argv[0]) or '.')
diff --git a/test/smoketest.py b/test/smoketest.py
new file mode 100644
index 0000000..a2f0170
--- /dev/null
+++ b/test/smoketest.py
@@ -0,0 +1,71 @@
+# 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.,
+# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+import sys
+from cStringIO import StringIO
+
+from logilab.common.testlib import TestCase, unittest_main
+
+from pylint.lint import Run
+from pylint.reporters.text import *
+from pylint.reporters.html import HTMLReporter
+
+
+class LintSmokeTest(TestCase):
+
+ def test1(self):
+ """make pylint checking itself"""
+ Run(['--include-ids=y', 'pylint'], reporter=TextReporter(StringIO()))
+
+ def test2(self):
+ """make pylint checking itself"""
+ Run(['pylint.lint'], reporter=ParseableTextReporter(StringIO()))
+
+ def test3(self):
+ """make pylint checking itself"""
+ Run(['pylint.checkers'], reporter=HTMLReporter(StringIO()))
+
+ def test4(self):
+ """make pylint checking itself"""
+ Run(['pylint.checkers'], reporter=ColorizedTextReporter(StringIO()))
+
+ def test5(self):
+ """make pylint checking itself"""
+ Run(['pylint.checkers'], reporter=VSTextReporter(StringIO()))
+
+ def test_generate_config_option(self):
+ """make pylint checking itself"""
+ sys.stdout = StringIO()
+ try:
+ self.assertRaises(SystemExit, Run,
+ ['--generate-rcfile'],
+ reporter=HTMLReporter(StringIO()))
+ finally:
+ sys.stdout = sys.__stdout__
+
+ def test_help_message_option(self):
+ """make pylint checking itself"""
+ sys.stdout = StringIO()
+ try:
+ self.assertRaises(SystemExit, Run,
+ ['--help-msg', 'W0101'],
+ reporter=HTMLReporter(StringIO()))
+ self.assertRaises(SystemExit, Run,
+ ['--help-msg', 'WX101'],
+ reporter=HTMLReporter(StringIO()))
+ finally:
+ sys.stdout = sys.__stdout__
+
+
+if __name__ == '__main__':
+ unittest_main()
diff --git a/test/test_encoding.py b/test/test_encoding.py
new file mode 100644
index 0000000..34003df
--- /dev/null
+++ b/test/test_encoding.py
@@ -0,0 +1,62 @@
+# 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.,
+# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+""" Copyright (c) 2003-2005 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)
+"""
+
+__revision__ = '$Id: test_encoding.py,v 1.6 2005-11-02 09:22:04 syt Exp $'
+
+from logilab.common.testlib import TestCase, unittest_main
+import sys
+from pylint.checkers.misc import guess_encoding
+
+class TestGuessEncoding(TestCase):
+
+ def testEmacs(self):
+ e = guess_encoding('# -*- coding: UTF-8 -*-')
+ self.failUnlessEqual(e, 'UTF-8')
+ e = guess_encoding('# -*- coding:UTF-8 -*-')
+ self.failUnlessEqual(e, 'UTF-8')
+ e = guess_encoding('''
+ ### -*- coding: ISO-8859-1 -*-
+ ''')
+ self.failUnlessEqual(e, 'ISO-8859-1')
+ e = guess_encoding('''
+
+ ### -*- coding: ISO-8859-1 -*-
+ ''')
+ self.failUnlessEqual(e, None)
+
+ def testVim(self):
+ e = guess_encoding('# vim:fileencoding=UTF-8')
+ self.failUnlessEqual(e, 'UTF-8')
+ e = guess_encoding('''
+ ### vim:fileencoding=ISO-8859-1
+ ''')
+ self.failUnlessEqual(e, 'ISO-8859-1')
+ e = guess_encoding('''
+
+ ### vim:fileencoding= ISO-8859-1
+ ''')
+ self.failUnlessEqual(e, None)
+
+ def testUTF8(self):
+ e = guess_encoding('\xef\xbb\xbf any UTF-8 data')
+ self.failUnlessEqual(e, 'UTF-8')
+ e = guess_encoding(' any UTF-8 data \xef\xbb\xbf')
+ self.failUnlessEqual(e, None)
+
+if __name__ == '__main__':
+ unittest_main()
diff --git a/test/test_format.py b/test/test_format.py
new file mode 100644
index 0000000..94a499f
--- /dev/null
+++ b/test/test_format.py
@@ -0,0 +1,168 @@
+# 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.,
+# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+""" Copyright (c) 2000-2003 LOGILAB S.A. (Paris, FRANCE).
+ http://www.logilab.fr/ -- mailto:contact@logilab.fr
+
+Check format checker helper functions
+"""
+
+__revision__ = '$Id: test_format.py,v 1.13 2005-11-02 09:22:06 syt Exp $'
+
+import unittest
+import sys
+import re
+from os import linesep
+
+from pylint.checkers.format import *
+from utils import TestReporter
+
+REPORTER = TestReporter()
+
+class StringRgxTest(unittest.TestCase):
+ """test the STRING_RGX regular expression"""
+
+ def test_known_values_1(self):
+ self.assertEqual(STRING_RGX.sub('', '"yo"'), '')
+
+ def test_known_values_2(self):
+ self.assertEqual(STRING_RGX.sub('', "'yo'"), '')
+
+ def test_known_values_tq_1(self):
+ self.assertEqual(STRING_RGX.sub('', '"""yo"""'), '')
+
+ def test_known_values_tq_2(self):
+ self.assertEqual(STRING_RGX.sub('', '"""yo\n'), '')
+
+ def test_known_values_ta_1(self):
+ self.assertEqual(STRING_RGX.sub('', "'''yo'''"), '')
+
+ def test_known_values_ta_2(self):
+ self.assertEqual(STRING_RGX.sub('', "'''yo\n"), '')
+
+ def test_known_values_5(self):
+ self.assertEqual(STRING_RGX.sub('', r'"yo\"yo"'), '')
+
+ def test_known_values_6(self):
+ self.assertEqual(STRING_RGX.sub('', r"'yo\'yo'"), '')
+
+ def test_known_values_7(self):
+ self.assertEqual(STRING_RGX.sub('', '"yo"upi"yo"upi'), 'upiupi')
+
+ def test_known_values_8(self):
+ self.assertEqual(STRING_RGX.sub('', "'yo\\'yo\\"), '')
+
+ def test_known_values_9(self):
+ self.assertEqual(STRING_RGX.sub('', '"yoyo\\'), '')
+
+ def test_known_values_10(self):
+ self.assertEqual(STRING_RGX.sub('', 'self.filterFunc = eval(\'lambda %s: %s\'%(\',\'.join(variables),formula),{},{})'),
+ 'self.filterFunc = eval(%(.join(variables),formula),{},{})')
+
+ def test_known_values_11(self):
+ self.assertEqual(STRING_RGX.sub('', 'cond_list[index] = OLD_PROG.sub(r\'getattr(__old__,"\1")\',cond)'),
+ 'cond_list[index] = OLD_PROG.sub(r,cond)')
+
+
+## def test_no_crash(self):
+## crash_str = """wizardBmp = ('eJzdXc2R5DxyPZBR0WPBd+4rywOZsQasCevDHHTVdQ5jx1w3QnJBF7mwISdUUyAeXv4CIFndPYpA\ndLBR+H14TCQyAfDHf/7vcvu+h7ef7TkKI2leEU7WW7K//3r8vf/jn78f3n9tf/+f3w9vPx8P+zMi\n3389kpWUj7/8a3lWkSUll/NDAYv2lwc3huPLw1XPF4JsCmxQPJEEaMAZCRg0HgU9IiY7gy+A/X8T\nAKnk2P1v/2ooPZ6B8CM9pWTiWdgthhbtPw9Yl6v2tZKQ/u7s3/7117/9twva+vZfO/Ge8LoEtrxV\nXLWRfxiwjJ78+8Cn4FmoWB5AUX6wHCsEBvKPv4/nndUmC1hdfuWUoYx1mcBksCKRH87Hx+E3bslP\nt++/iVeRVJACKyaeJbAFTbzg8Yi4kQD20bAS0No/KBSKtn+fHSyB/y3PJc0DWPzL6UtRKjGK5QdR\n/tvPUiB+3YE1YPK/zJkP+BvVruMLCeltLZElBgRGZMmFlFwOMxzlFOI9nguTS2I841euCA8A9tMp\ndz4wxxSjkpQq0s2r0nf/ZcZ+OiyzGIKr65MnJZ6nG6YTXlIbOGURvCoSNUIsc43lGZF4gLr16VgN\n4snPETntw7UNyCUwIiFjPx23PEAdUmyEcGMxuFZWd8u00tjNhZQQrYrSiET2PwXYSOi5M77i9mD5\ng2m4xqSELwWs2wyQihmrmFa09DXQClQAqVhmPp5zUcw66moYi2Qo8zewX1JxtfNsxEyXQonUdXWG\nKcYmCb6+jAV/mHjMMZa0KqXSYJWqwM8Rq22A/GSt2MX2d5n/+OeX1QosPSLVMS/Bpsz/TUqbyvjV\nGMvLKLUggpKZCEMWC0oalwQR1fMmqdcnJy0D++l4JiitwxRVedeA8LZklT4h5whpF2ndmu51bFtQ\nkZGFyranTO5LsGafClBrFf9R5m5rJQWYLQ9qkbVIQ5ZaZK2kjaDM0GzIpjnXFsrxbjJVQgxv+asM\ndMXKx8ZVkZ3El1va4y8MfevTlL13v5qvuUbXEdBs2lIitbaRnRzDBMxDn9dLzdSENtN1qQb5b//+\nH2Vi3Q37yoqrHiK3QrEBPg16rpcqisQQPJphf2W3ws5zeBAiYF1DffdX+zCJMBrGjo9Hwwq8v2Oi\nVnVrJPeW9RuWRYFDPE4pueqyGrKCIz/TNVNNyuw+fjyUzha6alSnCn8CCwyVwxpsdF3bEVxKxpah\n55S7p+ZjgPVcPPvMUvpVnaT7orXS9fH3d/OemwH6GJrgOlv3yGcb9hrzlMbx7Q5Tf1/BQIPbT/lf\nCezvYa3/YtJpbX4+lyYVSBuwg6ia1iovbakFD3t71MRXFVQFrHJt20kQwIIGrro1okodVsygBbGF\nudBgb+Fzc0VB9XdT5XBwsa7mJnSMqhuwCFX6Q6grkuZgtTWhYsn3sWT/9AVCa1hRzh+oPl2cRRUs\nNqKz5c+vL1yQo/jFWz58CrCJgl2wLTMXRMExHApFS4xyIB4YGoiUe91CkOf6AGBL+RBiPL6LWSFi\nKm9awRhjlbjgks9wdbYEJQpeZITBXAZdscynK/k4QAOGSKlb3V5gOVDECK+V8FKcIe0amHSShr2a\nsUXxKChh6HmzhOLDozGPX4UoGBh0aK0F1aKkrVdw9XAhr2Zs6WYBimdYFllIDIgEsFU7CiF9ZsFk\ntyvncheHqmK2I9bdM2g2fBWwT/qVN7qpT7H0KxDxykuld6tgkpeMyHUJY21rR4o9IwqUNUk9rCRj\nuddlblqlAVlhVUZhRCvYB6J6Q3a7jXT8RS0fD+yUAWP3sQuKermMrQYBy1urFayVgV11q+AJCcCj\nBpV4kBhDKed1jA+YvPb5tMKF19yn65Pk2gjjLrvMEMB+Kyyx80ZyN0CfwbL3k4Et1HoaRZm35aH8\nZNPnMhavgJitnlqBVYyvosdsma8GtlBot3w+5ZLimLJUuKJAmzJqGraHN7NqVTngXrmmF9JBuSvh\nsZphtYJZwZ6nb+vBqmo++gvLFa+tkEBPXsoJSCYatkirfb94uEThsatFVgJdeH3XjOvwcl0ksUUR\ngg4PZQlWDFY8lzVrdrW5hYZuT8vdRQrZCblGYcyMrJoqjQwFKMeDkHr9Oj4vi21uHUWos3NR0dkk\nCGGoZ3PZKiUKEPSIPDO6ptf9TZltuUcV66DZnZuqZHp+iQehTlULuTbge2Vyuig5wFb0xFjcvqN8\nB1iWP6e747hvAGwQXuWacU+uPW2tnGaROhjM2lB3W/OCWW/xnCn7FNOVWpPdYV+kesVeCyy6YHxz\n7LNQwC71MGa3JTCwNNrfGmm5ImxCuLBrjoy9ENjcvVWf0QZ1tCppzKA3VnC11gX1WIiC2wBXXX+Z\nl8vuQBCv3nlgRxoZnzW7piLZX9fft89cl1bkvxQjwLrvjtpw4oZ8cPnY9dXAlnHUbuhECCiNK1Kx\nboa3kSiI8AGwqa47skZo6g0AJFeRiLw16aqUBIWYeGABHpVv/62ehbWag/aF28zaga067gLBXS7l\nwEJDTgDHK1nmcUzcWPJzJOYpbewqfrGKalEkmgKAgasKA8phVQEVFa1g/7Xu/UOZC/zCqfubQ7Sk\ndZEpz4dtBUt1ES5Pc6u2MkkLSRSJiR5t0Cpr/bVwuyw0GFJeow014ykbeZX6onAMWDXc6F1pPGwj\nI93czCG+xawFdkDqpGDLnALWdiF6nRVpt+ETZGs9NXNydEAnyLfyzH+1UJVyVb0LEau1gK0xXLUj\nabEwOdrTRRmCXuyaYSha78qOrEqwXKtUhax1ZgmJx6XBzvOsJdJ/0LyIioPMWY1r5gMYq8ax9J2f\nxZueOwff9vtDYCjQb30ZMpqdudjlNYZuW4VbnQbWaAWd8oM0apMbRzJhwKJWYNH6pGkIVi9oF816\nUFG9zx/XOhYi93cC1yWigMdUU6hnBme9CKuVBuyt2Wq0EYZk6esgXc1LMRgsYxUUg0uG4nxRXE12\n9TA5oUE1yYwDCDQBWU24tOpeT37Z6o5JOUc1pRsSlt6OuKbHnt4nqf4dYRELUiE5pZdWKQ9aW6i8\njRpzVbA96lY0KwoiAi/m+F5YQtWXeEpi9Hjvlp3l1VzGRphXQFoC/JKoqKvKHl950fqlLZ8H6Fpw\nYHxAy7W6FMHJxThwF2kb/1G3KLxa0q5S2A4ytpkp5CJ6lRSN7AZF/qxmA7xumJSfanrigN2Y0FoZ\n2IV2MAodjPQ6tnFdAilPGcpQYCm31G2cC2xf1rYmjWyigzDRkDFtrYcduF5ec4GNTYp67zsrCaiu\nFFVmK7VcVXYz0XJqreqOk9IzjYqtvDjHEZnHI2Ddurzul594T+YiLbGahy4UEbBxGtjwHQOLJUmE\n83iQzkRYt/Jc7gQxF8hlAGuwILaEC6JA2A28IUj8Nfe6Qwlnl6LJ7ppgTtQmrPCBTTchRAN6V6f/\n2DNS3dx6tkqD1mNtgupML/Mg29PYB6THN/dtJV5dewg3cKD4wEaeC9MYTN8LzCy0P6TYUVUtP5Q7\nuzfc0ApssK49a0V0sB1H1fxqN2w0GRsU2xpvJLbSE8QY0aqfu7nW7Y6Kez+qeR8czvqolrQRsM+H\nuzl7K96L6MKEm5xBeu48vIZ362HnlFQyGi+0lBhbq26V+QsifbcGV6qUOcVFyVXGwBBfxtaWN9ce\nWSZZ3+DsbtdGWMSdvcsjaUrXsiUoW8FhNY/XCAoo0c2qs4VFWcbaJfNbdQsSqWCsEHPZpSaaqbWz\nBdaCvJgVAWfh5R5sa40k5kXOUW08lyRHGixGVnkhnhIjwg5mmANCul1Wv6JHS90utcZLWmS8ymwY\njSCE6ng5i1S3wi4wf0gPKaYGsbgzQB3r3ZT90AW22ww7oGDOMWPIIlUjPbmb9tzpLLbzgkgLD2tu\n/ZYEo3CXpx1dKPj5pIxVYzfivuwWuMiV1xoTxtp8gC3ztiwi7vViBNvs2W6OhPOiwI7j9ndxzTKP\neDdykc70osKtZM1WWaCNMIF31aiqFne6YSZsmzkRg4sobY2rfDcRyTdPXqIVHBtWl95lndtsrdKG\nFlWneXtrbnFlT4DWnRei3hEb6b4+nFgBaxWAbrAE2OpnBXJyupHMNJgUVnnNqaUKqNvKKT5xcycS\n+x04i92McTdnKGyN3N+S91rG5pI2Z7I+CU6rgx/VbWRJYqll22DnMj6tE7EuomJSo5v9vIxly49i\noNi4LkdcybrdxFr3XTBdN3mzgaW6uvsU5bS2kT1BXZUG0/Hq3Z2uWG0vP2cyYzcO1imX6LFq7BQP\nRziw1lUVb1NUhajDXPZdsHJ75+17O6fDvI1kqfuW5UJY8fa8KGjDRzO4KzlHIsuDqxW40/FGn3tQ\ndFJUX8ie8MAWhlzrtVcEhnqwmtdwNS+suEo6vRtqkKg5V5OMopC3fR9sMxu+/XRJstCJzsiTqEU9\n6bfgbZEMlqJWAViMq3RExgoDmjKmKeMSLGNQJOqQCeEWGBzsT9r6pFZetAWOG8+EwWY8VZ1rAGS7\nkJAJXIU0cPErs0jvnq0IkWfdGdIf1B6OeZd69tjVHL1k1x5o2Q9GB9O8kuHuGrp7Xchx6zZSjaOo\n9Ci8vKRVP0H0cTfvfLAr9xSYWrQdrCKvRmGRPosueS5wwLm3Jp4rM3Mmvu1HdNtSOj8pk7Zc6WBJ\n4tY1OKZ73Ah/dZuq3BA37aXFUv1VwN6O+ExzBELeco3VKbNf8ztQbANKerX8mGfilexIdzLCYNV5\nf1qeWzK3nGBmyfduLdXnxcrPSE9tUHtIxNpBcqn59UizgjdEgaOAnVWxxPxL5rhBRcsviuihjKht\nFNg1oxYaN+k9JP0NJS/yHAROT4CxRVVAvUIbfG+n9bt9ObbyEtZdug3+7m1wmgZWigLWalCjWtJq\nlbVeHM0vuHIrqEh2QVrD2kp3zq9jZudLlrSaOYd4y/pSWyDc/FUhqogq6p4Fs5H2hpm86hFguf2u\nb6K7HGvLRnMJZANN/gWSIrICWyStW+9Gl5dinSuqo/0MvDrOgb3X2+ybmuE5TGHTjrhxOWPV82Fh\nu6iVl1pSYYHJN1FbKwpd39FOgSkZiw2K9LEArPXcDvLyNsfhKg+CayE5Ir3V5BWUAEloRSKviztq\nmxvkC8UdtHaeJPy4wjssqDKzqyHCVqhbnk3vToeUMY+3BJWNTWASRVfPXtEFYfZNtIw9BjKbiDGs\nhxUDwTSzUcRON2oSV9pFM+2aI2aq6ryDdkdKB9jrGOsOKBuCBksQwEozeDKPcwkQs2zTPtnNXA9x\ngB1g7IhWYA+crvUKPlUOy0NVS0GyiQIoDwYoNemzClHKKXoF6126ruGeQlVm67ebEdkv0Qp4QO2/\ns5J2sdatun9PM9AcZ26+G57CsHfxsKLSc3bbcJVWUILycbhKZhdVLQpqDDa0sKsRhgXe1bxKw6wa\npmNdu9dNHevYPeqREn5mZFk3OLIEMycTFZ7CllVPMTeVtYpl1g0QSoIzHZySsVcBW6pmvR1Kwjiq\nSt2yq/tG4+oS4vWUVU3vtNdISVpRdTyJ3+m4LvYdjQAL3ViYW61Zj030xrq42h1N9ZMH69uw0+St\nnaV1r0ABD4Whm+Y1t1jf3Jp4c3hTN94LaJLwm6dutYlVSTfIL7jBrXDvnYqyrbLAqjR89kGcpwvq\n0uWMexzgs1BfqUvdFhcDe/NO7sAuOgMsXKhaQ6CAipRFxRcIJFI0bj1goWJ1TOtU1ClglfOuaqFq\ncJuwHcT2OVsJ33SQEhUV0LpZEv7rfkkmYBrl13AnTMyN48CqYSIvGMN7p2tOW7PTMpWMTYZA9VpP\ncKaz4O0cZ+SFjZoknph1Ji/r85IJtNBmK+utqcT36n3b8DU9aPtqIGTgc1sKQxcBJGNgXdGxkEBo\nFkLbfdv3cuSqXkcJo4GTuD6zUr2adVnyb/S3BDG41eDJKorKaIf7bk9/G0j3iuTV345iJkvGgkIV\nZXvK6tZChm61pHWhuHblxdqs+zyox7ohqYsDjANlXYZXCWsHQelhuxDXtVaLUJLlKrOhDe5WwBE3\n3CCeXFHhPHioZihRrPwyBaSTaIC3Upj1k16+8nLH+rD1Y7yW5dacLEqzYmD/st9RmrHmzS5pLxcF\nCO5W2POnlmwtaiusWi8wttCaGqs95ynz1lrvXW+pBfZFqEbhcgK3OZoOy7ACIIC9SZPvcGNYwHYT\nK63gFWGTHyk4YEsc7LXYpksbNlQHm+KhFmLpa7s9750oz+My9nWiIALBfT5fLNQ2x+Qlty7wKvgu\nNx4nbb5cxp5BXp0F4O36KPySkb2bczrWJqYWC67+EBXO/qYuVh8jY4X6Fw/9JfBiSaWukEI3d4X2\nrd2cNu61WYYN+B8mCtTEvZL/8aryMXHvMXKF66q1yjExUvj24iv4zoSp6fVYwD4iV2WFHFaKWdLg\n2WZ/MLDs2jiz4TMKUDl4v7cyHaxvjquxJYi9M2wxGAT2Y7BVAlaJ2TPsZTSwvUHptG29UP0ObEiH\nL1KVZhvMpLWmJAtsM53FNkbfqJgGW+Miz9ew2xGJHVuc/eslWGmtt8nLo37QxV9AMvH+8JBxmVA8\nRGuNFfR610warGWGtSNnKA8EOpiM1Sujx1wVuq613wIxa+i2tmXZ+I8D9m3f9lOmDHG6h28JUy/O\noVq2emECgF1od8Emv6USOjqZh/IDTO2Ql8fVjwaWGsx0Yhrg8ObZKmr3Q0/uoIMGDVYXjaoDdF8B\nWLwvEALye3kNW9c3NBMAAp/M0v3ND4jV2u/18NpGdxqEosNWlExV7t+o2DEo2IUEGuy8lf6mY6iW\njm/qY2rjBJD488vVjuy583sXWBWfwKseBmlGzpRNfbYSlDgjFiJgXZbSHKqO6zqCxRC4RVpgLSYW\nLiSw/XUVjwFY3B0ITGCn0kjvMtWBWg4+GFl58JkhVSYd6Abtplyp1bRyCrBcy3MgmqCjyPYX8RzQ\nRykhrQLPWjd+5epUgsVbOCyR68QeYyT2in8h6lWbbdcsCHLVYBu84OqSeplSGYhuKAMnYiijUEqN\nZ9wNd+/QzVpXwWpb+OASrPzL+0a2+AYSVq1h59TLDe92lI0Ona3VslQ0OnHEqfrjwoE7GibWp4YP\nHH8m2FfApVz4ZkmKcrzN+6N+6oVZh9XKeHApjfjo2paIvWxLYY6tkxYb5hge1EsBccqRK1ldVDOU\nnSeKbDI2ES/nglBND1kLx3NFLHJp5r4Obi2Wn1G9+JcZ60rL8peFaiSHE/msWD3COpZdTCcl5SxF\nXTox8fhZidOkYV3XDBe+4btjPKHXSXBX1M/J1fDXN3HLQU7CEWrlRHKla543eSOS9OggLOfQAy1R\ncU8g+MYcYIkKzRlX5zHblQzf6JIl2zy+VqvRr95JyFvsUIhqWOmgldgLmaoGXx8laRO/zKK0Aitj\nFYE5UvJtlRJshLFW65glScLAXK4mhXd5m4tZFd9WXnXN3rQCXP5Z1VHWYIEqrrBrbKx67FqV/53A\npEKwyB1xKbJW4E7Hrhy2WywidTTnLbIgZSRmWSsQjK0TN4+1+ms55ovQSfk8wpmEIYM0Vg22uaI3\nKBHXKr7J2EB9tStTsbaSOz+V4nqvlzbPLd964s7O4As50yNW23Igt62Oam+YUa9MQlcIuo2+Rhox\nJxlrN17JTy1O5WVHVhR3eWslpyVSLleTMpPXIeGq+6vL2H19DeFQDSz3pz1hI68K7BjuWkz/K2Uv\naKBflmog6vIWZbIagDW7K045ICXzdqMlv2LjGpxMjFZe5TLVe71qpvzLwY2cSnbH95KM6Q+ocuLN\nGk6VeZAMthuZ71iJ2iPrSN3plDHk/51chMgF5nBilRKjwLW3csgFPAJdNzzKdEtr9A5CyWhLE9i6\nFmA6J8gOKTbzsnULkVt1Fm/kF9tgb68vLA/QpjwIVE6Rq+y+ZGDd3p2EtAFrbdTSQFpaLkjufqZK\n2c/f6PQB2VvQaziFl7qL3uUYp4QkKS1vznHpRXr8uosCHJAkVsNliUuV7YupYiIJ0Eb/9t2mGXSm\nqIoavJHzheQ8BKYSAou5pxSALPVKcE5Qfv1WL0NmVuMZWVRKUB2MFa19ekyEzl+xVQ9tLpOGejsW\n2UtN4ZuU9uWvz1tLYJbbUuK1d7kyHGwUMpYOkCoBrsRLGcTSQjCcx07seXYbT5E81wNDN+VSD7rq\n+Wgs2KFs2aVvq80vyuf1vttC99eTxAVDhyH4q34OrMAViVPmP4QAAwtpA7Ph4Huquxm9oQSOYLXn\nCHOBZUg5OzIuZkOafYaGyXqXlcbKaKNSKl3OUTzo7IO6VR7CqjWMJRgj5i5dXRft+y+dZWBDvngR\nTF1dVX9kFbB45hqOt5FLuoJwy+R9BVl/2Wz4D/qIp3v6DAyn0yt3GBUjB6s9V1tlYMnrKvlJgXlQ\ntsS1nosBAznZ6i2iVXb3yx3LyLoPFBpI34wwYH6wFg7rYsaS0zmvdBxYS8LouVuIjVzI/d1/T+t8\nhOl1CNs6R8MkPtpxyNtC+2ozHy9hFuprw4/hK/gOEI9HcJx4IqMn2D8XsQlgR+hauYeQp1SRELbb\nmFlbFXWXzh1M3F85RMA6Me/CXzNVi6VcUpHOGJC22+CvCawbYDa0go7VyCQjpvgMTLcE6dCZkrSf\nCOxgYqXXTeVSknYq+1rfF74044uHcVGwVlk3OzWv5Ltp9of5EsD2P4K004ydnNlF3p6c/JhCPhLY\nQY2L5+V1fr5gsySb6IcgJfWAbQgjuT4N2N4uVqiOYo42Y9HJXs+ucphQvd7N7pErDAivBTY3apFN\nhk15i7Uxps4XCElrod1H7Ub+BXVtGn6qdtFmP1fNUMZDjkzsRd3Gcxqb1ws+sEEuAWyUReWlfx8v\nMnsc7s+zV/3uqCCxFX4xbsMIUG5Pj6E9CKybkfwmRxgrSxjy18TjKwqJWjKIbbf9OfhB5BRjmSqi\nO8MdUf6ajh82LkRhK5g/0pguLd33zs01zti4SR2yDQBbPBdnSfv0nlgP5oGBPtCFTt5awtSRT3ZP\n+33Jw1MN0J7xqUJqpZ0BGgTWZV1O5ngGUWEKWLyD/is8hu2oa3u4kH0DQILb1E8Xwesc+Yyn1FAr\nUGpMGjJ9abwcIr8eoEihsj/lapjRAHVe+0BpEutWy+J6FW/fc/1c4wMAlYb/jIxKWNluBnBUdunZ\n/PSlgVggkE7udK0CItyCdTiQxS1BD1N1tXzDTrnAIxC1RzG/BDid3fa7MVy+ikxGOXlWRf0Y/nIH\nKHpmLbnT2DBtpDtOY7icD7/kKg85Y1Ufuya7hCENEOmUjMwFSXsKb9UeciUHRkiYj6ZtQPScADtY\nnbZXHyPtRYaUL26NEdd8GazEGNEGDHXBiDu+/tDc2sU1TP5EMkeoiutM65mIrwPvA9jxxIs8QXAs\nCOafwIFt3V/Q4g17bHd+hFlpi09zd4EqqIoNosmNgimNxfmF4QsNPhRYvNRyW0VoW/ao4iSW/66u\ndI0CXo23n/zXrWj9qoxVG4wh+pS7mfVG3l3pBpUsKcdJYyJVdlVFxFgr/KO38rDmkDy0GzZScg4x\n1gssNGbLn23Mlwr2voKEfnaHvCWSmz3hs3qIgq2IG5NvEP0sYN1eqH+/mTM1KrGLjH2XN3O4Jsmb\no81HS+7YZJ5YAnMbS2L1covq/W1HPtlComwmNV7bYzmjNdHwgyxHHbfR5XAD7F9ZSDsA4rbEDYNm\nwK5tMDXBzdpjxUkWi143vO+HlTTT5gPztu/lGcTqWPAIP+tBQHdCunaB7TolDwF7uJwXhQOumWZY\nPtQX0DW0/E8C296g89hexeGjwJ4HpDH/dDnZPgf7kkbeATcyD1Fp88DyLSWHacBqRjt9M08YuHjK\nA+JdTb67NFAPI39XubKwCwQMkFXGFngQWAfAinVGbxSgHdXtYR9rhVB7Pl13VXqsHWVnFIgkMCwf\nqE4zjVw8E9iyyb0eqPmzgFWYnGeILuTYLlnm/NuRgzwfBmxuoMMzL1HVr24WW8JCV2rcyQk4Cmm1\nwglrDK4a/kqkHT/c0ehBRryp4Nt5puTAe3NbMGPXr2c5hCgY6aBi7IEAvQKMdfHPC+FTY7CT57k+\nnsyzH/E5SY9LbH2LNJufac+rgR0c0GatOnpihR3ohwthC+TEcHwsaQ+c8zqsFTgy9rTj+yOxOgDs\nYAeZb9mCIvhJ2cajlPnQKA320wHMgR3sFHhy4HjgUm76lXSdXhp8eY+MAnawj00FjV0h0ToaGuxW\nL7myWTqo1qsPeME1kou13HyZ7yY48LDUr9W7vWPolCV/irGFafjglJjNg616bglLdU22ctJNkoMj\nnuOc/5S0Odt4rAxouYE6N0xJt45jHFO2OCBGJbA3U/sLum6prqGP7YqqtOinKOb5NwTWA4rtJ45V\nOcZW+f46bhRvXNhZ0Dl0cPjviJE2GiYVM3XOS+IzAYs5hdRB1QzQZi5dPOwp+7BwwOd14JyX8neH\neAYlqHE543f4NGATUSk9zj4yXsbsFbbAmkIUpLMvy3g7h34dLn/oQ2k1CB/TiJB8/7WMu1Ndxpr7\nJ884dueQH0E4fsuO7Svwp3UPWLWtYo5vUg/59ryIdVY+d5LJ9Ju6zBC3cdZlY6k9k/Du5BXjj3rD\nSdnLiKtZ23AMkq22X+1u8nU8XgVE/6p4ilnpxviFL42sDW6Qyuum2cBuEWhfR5ILW1G76o7ckuTk\nks4XJ1RYwrVSxbatBVTVAysCf61hNkmKf+tKRKWJnpMqsI2Tlz/cTvViNv2cRlwsYSRdsRWBX59o\nWYRBZ66iUiZqtHSyf13Y3StMwVtck77Q/ZBsLN3kdfSLvLi+rNZ3YBWY7jb426gzUTCTH7ob6S2x\niaJlRE4aXlxCWr5FJF/Ixu6yGjFlf6y61E7jXGUIG2H8ZteU6lacUWtJ5SobBjdzhutAwFuprjC1\nMHJT7VWoeOZkMLlzSrGNUzmU337yACEIG8g7HQ1IwixXibRn8IxIeDjB4Puy1C/QgVd3u/taflyD\nTXbdjdw5vRVP7MbjIqyOcXUz3+/ovjjWDrnIrf6Kyav3YS/I6vZ1JLY/5/Rj3o4kjmWjklGqDecp\nKuRbXOBJua3KKV3grfJMy4i3ZabeTwWaLyLd5b53sCViXQFQlXPS1bjKLxmhDUpUIjF/88tWGjFW\naQIuY4WATeWkKzm7GcVoEhVRiBbamGHPTVhLMN278ZeQlqsAYy1LrQCxp7+XesA2lLR0ifRCS4Ol\nfjKDk935E41HNQE7gycJFlIVIlnqlsNZNnyZwtUKxshmJeexsNI17EswR1xC1KlkLCQTVkfvAgec\n8xIiLlA+mX4rLWAVaV1twZKTBfIlt8KiYYN6yCq/N7GZ6/oX+b0JK6LdlVdjbF0WRaKVh4bHFHqs\nEpJq6Eek93mKRnRKuMopbS79wqai2MazEWahjZptg/H7L0hFpmLBhFOym2+tS2B1UoBPzIEhZxZW\nW/0sFxOGLU6rmQQ38z1QFyj3u4rR5xSREmKzfPAX5FG2+mYCfYatumbu9StC1hXVbsOjBKyDtX95\nOhs0qLKNpVKITSLa0MqlkSsfZhYAtQRXYVjwRcm09sR4LdW6ZRml4FIHCcfj7U/NVDULqUmM+Q7A\najxtgRW0Mqz7ys4rHNzY8HVI22BOX1I+yy/zBb7nFeHTBTBK6f69xrFSDbYQShub9A2eTLO9DVWy\nZbXUz0ttfEoocJTsoqCu33dRcPq7fgld+e8FkBJDFramIt71ZUgG3uv3zkpMUhGMDNuYt52tu9d+\njJKZicESjqoc22GPFU+g43n3pYdieFpRm4CGh7v0GloBTAEuVph9IrGg+Ckw7LYqclGlxCsybeFv\nkOXeVfvF1Wd8y84tqbMkT/ROLdwA1PIESnlpI8We7SpJGL0a2ky+I+oWJl9o75s8vKMWwgCnBemB\njWpvc9yzIqvLqUr5LwZC6LG3+L4Cfk7+qsSSABeG7irg8ryzueyFZkOUS99oO6wjbBzJuytIM/f8\nI7AtcSrXbJb2Bbqxgzy8RLqcgXnVLYbW1EcKlNaVtmi9tDtNFJBfyZVRayxSVKsOtNAt2Q2Fpcds\n4FhoT1HdLmnH++ieQTgjwV4aPl60Hg4W2Fe/5meEyTF/TWKvHsl7YEQe78XUcaQ/N3wYXTEFTJ2l\nPVVjKpwHw2HGWlH5uhpLXbNnaT8lHODbuN17pKipQspKxIqCV+jz58NG23gOZD8mKktdxeI9m/dP\nYewZ0p6s+lhRfwSwWHAdE5KQsQdQ7eaK3m5XKzhgIXldgGPocHb1MJ6FGzCF6uqeTHQtda8L0i1l\nLQZs1FKGHZ3R/ksm8ea7qSlRPv4FXEvdQs/qhLWeOXbLGjl1auZzAhxPrruwF3jv1mgHCSIxmoP0\neIbPZ2yvj8WjGprxE19MZV2B1PEadHsnTeI6vWtXr/9mp2Y+HdUS6gaGzT10k3oNtnpWQmyQGK66\n5Gr+xxwWmSBjrG1z0qrxBgdDPASsymgdOrfvLEvbriH2FlmSq5j6dhSq71UbNFbeY2Aa0GesN7mI\nxFGW6G+PZioUu/FSPy4D0Yfn/YGoVeRqgRRu2fJQZnk44ttunLqvVbwd1QMu3LvgP6Vcq2Uy8nl9\nwcA9gs8UICy8E6bCxcj8dm4+c32rG3jWsmFDpcSWA0qpgHVrBLBoW8l+jSg4LI27BUoMMUc3xnIk\n8dCSijc1NWBl4dj+YYHdf72Rt7d+m6zIBMVt9nl9heWAUr93RfTNOTDoaPIycXswu14hbFGIFpKy\nFq56UXYV8u80VnhGmE/H1q29SNpOxpt2rIvN2z3j0mIOZnYt5Alu41+g+9zwkUvaS4IrChbrBfs8\n8I9Z7RCgHhwD9vAe/rZhw7i5xfxlFa3Lg9LH5Jvbb4MXuZKE5DMjTqVek/zax/rifhKlTKDtJ3ou\nWxFe9VwrKjGIV//mLYQa6ZY82KSSXmVXDdDtkTH/ByrXy2U=\n', (115, 260), None)"""
+## re.sub(SQSTRING_RGX, '', crash_str)
+## re.sub(TQSTRING_RGX, '', crash_str)
+## re.sub(SASTRING_RGX, '', crash_str)
+## re.sub(TASTRING_RGX, '', crash_str)
+
+
+if linesep != '\n':
+ LINE_RGX = re.compile(linesep)
+ def ulines(strings):
+ return strings[0], LINE_RGX.sub('\n', strings[1])
+else:
+ def ulines(strings):
+ return strings
+
+class ChecklineFunctionTest(unittest.TestCase):
+ """test the check_line method"""
+
+ def test_known_values_opspace_1(self):
+ self.assertEqual(ulines(check_line('a=1', REPORTER)), ('C0322', 'a=1\n ^'))
+
+ def test_known_values_opspace_2(self):
+ self.assertEqual(ulines(check_line('a= 1', REPORTER)), ('C0322', 'a= 1\n ^') )
+
+ def test_known_values_opspace_3(self):
+ self.assertEqual(ulines(check_line('a =1', REPORTER)), ('C0323', 'a =1\n ^'))
+
+ def test_known_values_opspace_4(self):
+ self.assertEqual(check_line('f(a=1)', REPORTER), None)
+
+ def test_known_values_opspace_4(self):
+ self.assertEqual(check_line('f(a=1)', REPORTER), None)
+
+
+## def test_known_values_colonnl_1(self):
+## self.assertEqual(check_line('if a: a = 1', REPORTER),
+## ('W0321', 'if a: a = 1\n ^^^^^^^'))
+
+## def test_known_values_colonnl_2(self):
+## self.assertEqual(check_line('a[:1]', REPORTER), None)
+
+## def test_known_values_colonnl_3(self):
+## self.assertEqual(check_line('a[1:]', REPORTER), None)
+
+## def test_known_values_colonnl_4(self):
+## self.assertEqual(check_line('a[1:2]', REPORTER), None)
+
+## def test_known_values_colonnl_5(self):
+## self.assertEqual(check_line('def intersection(list1, list2):', REPORTER), None)
+
+## def test_known_values_colonnl_6(self):
+## self.assertEqual(check_line('def intersection(list1, list2):\n', REPORTER), None)
+
+## def test_known_values_colonnl_7(self):
+## self.assertEqual(check_line('if file[:pfx_len] == path:\n', REPORTER), None)
+
+## def test_known_values_colonnl_8(self):
+## self.assertEqual(check_line('def intersection(list1, list2): pass\n', REPORTER),
+## ('W0321',
+## 'def intersection(list1, list2): pass\n ^^^^^^') )
+
+## def test_known_values_colonnl_9(self):
+## self.assertEqual(check_line('if file[:pfx_len[1]] == path:\n', REPORTER), None)
+
+## def test_known_values_colonnl_10(self):
+## self.assertEqual(check_line('if file[pfx_len[1]] == path:\n', REPORTER), None)
+
+
+ def test_known_values_commaspace_1(self):
+ self.assertEqual(ulines(check_line('a, b = 1,2', REPORTER)),
+ ('C0324', 'a, b = 1,2\n ^^'))
+
+
+ def test_known_values_instring_1(self):
+ self.assertEqual(check_line('f("a=1")', REPORTER), None)
+
+ def test_known_values_instring_2(self):
+ self.assertEqual(ulines(check_line('print >>1, ("a:1")', REPORTER)),
+ ('C0323', 'print >>1, ("a:1")\n ^'))
+
+ def test_known_values_all_1(self):
+ self.assertEqual(ulines(check_line("self.filterFunc = eval('lambda %s: %s'%(','.join(variables),formula),{},{})", REPORTER)),
+ ('C0324', "self.filterFunc = eval('lambda %s: %s'%(','.join(variables),formula),{},{})\n ^^"))
+
+ def test_known_values_tqstring(self):
+ self.assertEqual(check_line('print """<a="=")', REPORTER), None)
+
+ def test_known_values_tastring(self):
+ self.assertEqual(check_line("print '''<a='=')", REPORTER), None)
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/test/test_import_graph.py b/test/test_import_graph.py
new file mode 100644
index 0000000..2d2bf02
--- /dev/null
+++ b/test/test_import_graph.py
@@ -0,0 +1,62 @@
+import sys
+import os
+import unittest
+from os.path import exists
+from cStringIO import StringIO
+
+from pylint.checkers import initialize, imports
+from pylint.lint import PyLinter
+
+from utils import TestReporter
+
+class DependenciesGraphTC(unittest.TestCase):
+ """test the imports graph function"""
+
+ dest = 'dependencies_graph.dot'
+ def tearDown(self):
+ os.remove(self.dest)
+
+ def test_dependencies_graph(self):
+ imports.dependencies_graph(self.dest, {'labas': ['hoho', 'yep'],
+ 'hoho': ['yep']})
+ self.assertEquals(open(self.dest).read().strip(),
+ '''
+digraph g {
+rankdir="LR" URL="." concentrate=false
+edge[fontsize="10" ]
+node[width="0" height="0" fontsize="12" fontcolor="black"]
+"hoho" [ label="hoho" ];
+"yep" [ label="yep" ];
+"labas" [ label="labas" ];
+"yep" -> "hoho" [ ] ;
+"hoho" -> "labas" [ ] ;
+"yep" -> "labas" [ ] ;
+}
+'''.strip())
+
+class ImportCheckerTC(unittest.TestCase):
+ def setUp(self):
+ self.linter = l = PyLinter(reporter=TestReporter())
+ initialize(l)
+
+ def test_checker_dep_graphs(self):
+ l = self.linter
+ l.global_set_option('persistent', False)
+ l.global_set_option('enable-checker', 'imports')
+ l.global_set_option('import-graph', 'import.dot')
+ l.global_set_option('ext-import-graph', 'ext_import.dot')
+ l.global_set_option('int-import-graph', 'int_import.dot')
+ try:
+ l.check('input')
+ self.assert_(exists('import.dot'))
+ self.assert_(exists('ext_import.dot'))
+ self.assert_(exists('int_import.dot'))
+ finally:
+ for fname in ('import.dot', 'ext_import.dot', 'int_import.dot'):
+ try:
+ os.remove(fname)
+ except:
+ pass
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/test/test_rpycompilation.py b/test/test_rpycompilation.py
new file mode 100644
index 0000000..dd5929a
--- /dev/null
+++ b/test/test_rpycompilation.py
@@ -0,0 +1,50 @@
+from os.path import isfile, splitext, basename
+from os import system, remove
+from glob import glob
+
+from logilab.common.testlib import TestCase, unittest_main
+
+class RPyCompilation(TestCase):
+
+ def setUp(self):
+ self.trscript = self.find_pypy()
+
+ def find_pypy(self):
+ #trscript = '/home/adim/local/svn/pypy-dist/pypy/translator/goal/translate.py'
+ trscript = '/home/syt/cvs_work/pypy-dist/pypy/translator/goal/translate.py'
+ if not isfile(trscript):
+ self.skip('translate.py not found')
+ return trscript
+
+ def _compile_fail(self, filename):
+ status = system('%s --batch %s' % (self.trscript, filename))
+ try:
+ self.assertNotEquals(status, 0, "%s translation succeed !!" % filename)
+ except AssertionError:
+ exefile = '%s-c' % splitext(basename(filename))[0]
+ status = system('./%s' % exefile)
+ remove(exefile)
+ self.assertNotEquals(status, 0, "%s run succeed !!" % exefile)
+
+ def _compile_success(self, filename):
+ status = system('%s --batch %s' % (self.trscript, filename))
+ self.assertEquals(status, 0, "%s translation failed !!" % filename)
+ exefile = '%s-c' % splitext(basename(filename))[0]
+ status = system('./%s' % exefile)
+ remove(exefile)
+ self.assertEquals(status, 0, "%s run failed !!" % exefile)
+
+
+ def test_translations(self):
+ for filename in glob('rpythoninput/func_*.py'):
+ if filename.startswith('rpythoninput/func_noerror'):
+ yield self._compile_success, filename
+ else:
+ yield self._compile_fail, filename
+
+
+if __name__ == '__main__':
+ import sys
+ if not '-cc' in sys.argv:
+ sys.argv.append('-cc')
+ unittest_main()
diff --git a/test/test_similar.py b/test/test_similar.py
new file mode 100644
index 0000000..457fafa
--- /dev/null
+++ b/test/test_similar.py
@@ -0,0 +1,56 @@
+import sys
+import unittest
+from cStringIO import StringIO
+
+from pylint.checkers import similar
+
+
+class SimilarTC(unittest.TestCase):
+ """test the similar command line utility"""
+ def test(self):
+ sys.stdout = StringIO()
+ try:
+ similar.run(['--ignore-comments', 'input/similar1', 'input/similar2'])
+ output = sys.stdout.getvalue()
+ finally:
+ sys.stdout = sys.__stdout__
+ self.assertEquals(output.strip(), """
+7 similar lines in 2 files
+==input/similar1:5
+==input/similar2:5
+ same file as this one.
+ more than 4
+ identical lines should
+ be
+ detected
+
+
+TOTAL lines=38 duplicates=7 percent=0.184210526316
+""".strip())
+
+ def test_help(self):
+ sys.stdout = StringIO()
+ try:
+ try:
+ similar.run(['--help'])
+ except SystemExit, ex:
+ self.assertEquals(ex.code, 0)
+ else:
+ self.fail()
+ finally:
+ sys.stdout = sys.__stdout__
+
+ def test_no_args(self):
+ sys.stdout = StringIO()
+ try:
+ try:
+ similar.run([])
+ except SystemExit, ex:
+ self.assertEquals(ex.code, 1)
+ else:
+ self.fail()
+ finally:
+ sys.stdout = sys.__stdout__
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/test/unittest_checkers_utils.py b/test/unittest_checkers_utils.py
new file mode 100644
index 0000000..380b4cd
--- /dev/null
+++ b/test/unittest_checkers_utils.py
@@ -0,0 +1,49 @@
+# Copyright (c) 2003-2005 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.,
+# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+"""test the pylint.checkers.utils module
+"""
+
+__revision__ = '$Id: unittest_checkers_utils.py,v 1.6 2005-11-02 09:22:07 syt Exp $'
+
+import unittest
+import sys
+
+from pylint.checkers import utils
+try:
+ __builtins__.mybuiltin = 2
+except AttributeError:
+ __builtins__['mybuiltin'] = 2
+
+class UtilsTC(unittest.TestCase):
+
+## def test_is_native_builtin(self):
+## self.assertEquals(utils.is_native_builtin('min'), True)
+## self.assertEquals(utils.is_native_builtin('__path__'), True)
+## self.assertEquals(utils.is_native_builtin('__file__'), True)
+## self.assertEquals(utils.is_native_builtin('whatever'), False)
+## self.assertEquals(utils.is_native_builtin('mybuiltin'), False)
+
+ def test_is_builtin(self):
+ self.assertEquals(utils.is_builtin('min'), True)
+ self.assertEquals(utils.is_builtin('__builtins__'), True)
+ self.assertEquals(utils.is_builtin('__path__'), False)
+ self.assertEquals(utils.is_builtin('__file__'), False)
+ self.assertEquals(utils.is_builtin('whatever'), False)
+ self.assertEquals(utils.is_builtin('mybuiltin'), False)
+
+if __name__ == '__main__':
+ unittest.main()
+
diff --git a/test/unittest_lint.py b/test/unittest_lint.py
new file mode 100644
index 0000000..ea111d8
--- /dev/null
+++ b/test/unittest_lint.py
@@ -0,0 +1,316 @@
+# Copyright (c) 2003-2007 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.,
+# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+import shutil
+import sys
+import os
+import tempfile
+from os.path import join, basename, dirname, isdir, abspath
+from cStringIO import StringIO
+
+from logilab.common.testlib import TestCase, unittest_main, create_files
+from logilab.common.compat import sorted
+
+from pylint.config import get_note_message
+from pylint.lint import PyLinter, Run, sort_checkers, UnknownMessage
+from pylint.utils import sort_msgs
+from pylint import checkers
+
+class SortMessagesTC(TestCase):
+
+ def test(self):
+ l = ['E0501', 'E0503', 'F0002', 'I0201', 'W0540',
+ 'R0202', 'F0203', 'R0220', 'W0321', 'I0001']
+ self.assertEquals(sort_msgs(l), ['E0501', 'E0503',
+ 'W0321', 'W0540',
+ 'R0202', 'R0220',
+ 'I0001', 'I0201',
+ 'F0002', 'F0203',])
+
+try:
+ optimized = True
+ raise AssertionError
+except AssertionError:
+ optimized = False
+
+class GetNoteMessageTC(TestCase):
+ def test(self):
+ msg = None
+ for note in range(-1, 11):
+ note_msg = get_note_message(note)
+ self.assertNotEquals(msg, note_msg)
+ msg = note_msg
+ if optimized:
+ self.assertRaises(AssertionError, get_note_message, 11)
+
+
+HERE = abspath(dirname(__file__))
+INPUTDIR = join(HERE, 'input')
+
+class RunTC(TestCase):
+
+ def _test_run(self, args, exit_code=1, no_exit_fail=True):
+ sys.stdout = StringIO()
+ sys.sterr = StringIO()
+ try:
+ try:
+ Run(args)
+ except SystemExit, ex:
+ self.assertEquals(ex.code, exit_code)
+ else:
+ if no_exit_fail:
+ self.fail()
+ finally:
+ sys.stdout = sys.__stdout__
+ sys.stderr = sys.__stderr__
+
+ def test_no_args(self):
+ self._test_run([], 1)
+
+ def test_no_ext_file(self):
+ self._test_run([join(INPUTDIR, 'noext')], no_exit_fail=False)
+
+
+class PyLinterTC(TestCase):
+
+ def setUp(self):
+ self.linter = PyLinter()
+ self.linter.disable_message_category('I')
+ self.linter.config.persistent = 0
+ # register checkers
+ checkers.initialize(self.linter)
+
+ def test_message_help(self):
+ msg = self.linter.get_message_help('F0001', checkerref=True)
+ expected = ':F0001:\n Used when an error occured preventing the analysis of a module (unable to find\n it for instance). This message belongs to the master checker.'
+ self.assertTextEqual(msg, expected)
+ self.assertRaises(UnknownMessage, self.linter.get_message_help, 'YB12')
+
+ def test_enable_message(self):
+ linter = self.linter
+ linter.open()
+ linter.set_current_module('toto')
+ self.assert_(linter.is_message_enabled('W0101'))
+ self.assert_(linter.is_message_enabled('W0102'))
+ linter.disable_message('W0101', scope='package')
+ linter.disable_message('W0102', scope='module', line=1)
+ self.assert_(not linter.is_message_enabled('W0101'))
+ self.assert_(not linter.is_message_enabled('W0102', 1))
+ linter.set_current_module('tutu')
+ self.assert_(not linter.is_message_enabled('W0101'))
+ self.assert_(linter.is_message_enabled('W0102'))
+ linter.enable_message('W0101', scope='package')
+ linter.enable_message('W0102', scope='module', line=1)
+ self.assert_(linter.is_message_enabled('W0101'))
+ self.assert_(linter.is_message_enabled('W0102', 1))
+
+ def test_enable_message_category(self):
+ linter = self.linter
+ linter.open()
+ linter.set_current_module('toto')
+ self.assert_(linter.is_message_enabled('W0101'))
+ self.assert_(linter.is_message_enabled('R0102'))
+ linter.disable_message_category('W', scope='package')
+ linter.disable_message_category('REFACTOR', scope='module')
+ self.assert_(not linter.is_message_enabled('W0101'))
+ self.assert_(not linter.is_message_enabled('R0102'))
+ linter.set_current_module('tutu')
+ self.assert_(not linter.is_message_enabled('W0101'))
+ self.assert_(linter.is_message_enabled('R0102'))
+ linter.enable_message_category('WARNING', scope='package')
+ linter.enable_message_category('R', scope='module')
+ self.assert_(linter.is_message_enabled('W0101'))
+ self.assert_(linter.is_message_enabled('R0102'))
+
+ def test_enable_message_block(self):
+ linter = self.linter
+ linter.open()
+ filepath = join(INPUTDIR, 'func_block_disable_msg.py')
+ linter.set_current_module('func_block_disable_msg')
+ linter.process_module(open(filepath))
+ orig_state = linter._module_msgs_state.copy()
+ linter._module_msgs_state = {}
+ linter.collect_block_lines(linter.get_astng(filepath, 'func_block_disable_msg'), orig_state)
+ # global (module level)
+ self.assert_(linter.is_message_enabled('W0613'))
+ self.assert_(linter.is_message_enabled('E1101'))
+ # meth1
+ self.assert_(linter.is_message_enabled('W0613', 13))
+ # meth2
+ self.assert_(not linter.is_message_enabled('W0613', 18))
+ # meth3
+ self.assert_(not linter.is_message_enabled('E1101', 24))
+ self.assert_(linter.is_message_enabled('E1101', 26))
+ # meth4
+ self.assert_(not linter.is_message_enabled('E1101', 32))
+ self.assert_(linter.is_message_enabled('E1101', 36))
+ # meth5
+ self.assert_(not linter.is_message_enabled('E1101', 42))
+ self.assert_(not linter.is_message_enabled('E1101', 43))
+ self.assert_(linter.is_message_enabled('E1101', 46))
+ self.assert_(not linter.is_message_enabled('E1101', 49))
+ self.assert_(not linter.is_message_enabled('E1101', 51))
+ # meth6
+ self.assert_(not linter.is_message_enabled('E1101', 57))
+ self.assert_(linter.is_message_enabled('E1101', 61))
+ self.assert_(not linter.is_message_enabled('E1101', 64))
+ self.assert_(not linter.is_message_enabled('E1101', 66))
+
+ self.assert_(linter.is_message_enabled('E0602', 57))
+ self.assert_(linter.is_message_enabled('E0602', 61))
+ self.assert_(not linter.is_message_enabled('E0602', 62))
+ self.assert_(linter.is_message_enabled('E0602', 64))
+ self.assert_(linter.is_message_enabled('E0602', 66))
+ # meth7
+ self.assert_(not linter.is_message_enabled('E1101', 70))
+ self.assert_(linter.is_message_enabled('E1101', 72))
+ self.assert_(linter.is_message_enabled('E1101', 75))
+ self.assert_(linter.is_message_enabled('E1101', 77))
+
+ def test_list_messages(self):
+ sys.stdout = StringIO()
+ try:
+ # just invoke it, don't check the output
+ self.linter.list_messages()
+ finally:
+ sys.stdout = sys.__stdout__
+
+ def test_lint_ext_module_with_file_output(self):
+ self.linter.config.files_output = True
+ try:
+ self.linter.check('StringIO')
+ self.assert_(os.path.exists('pylint_StringIO.txt'))
+ self.assert_(os.path.exists('pylint_global.txt'))
+ finally:
+ try:
+ os.remove('pylint_StringIO.txt')
+ os.remove('pylint_global.txt')
+ except:
+ pass
+
+ def test_enable_report(self):
+ self.assertEquals(self.linter.is_report_enabled('R0001'), True)
+ self.linter.disable_report('R0001')
+ self.assertEquals(self.linter.is_report_enabled('R0001'), False)
+ self.linter.enable_report('R0001')
+ self.assertEquals(self.linter.is_report_enabled('R0001'), True)
+
+ def test_set_option_1(self):
+ linter = self.linter
+ linter.set_option('disable-msg', 'C0111,W0142')
+ self.assert_(not linter.is_message_enabled('C0111'))
+ self.assert_(not linter.is_message_enabled('W0142'))
+ self.assert_(linter.is_message_enabled('W0113'))
+
+ def test_set_option_2(self):
+ linter = self.linter
+ linter.set_option('disable-msg', ('C0111', 'W0142') )
+ self.assert_(not linter.is_message_enabled('C0111'))
+ self.assert_(not linter.is_message_enabled('W0142'))
+ self.assert_(linter.is_message_enabled('W0113'))
+
+ def test_enable_checkers1(self):
+ self.linter.enable_checkers(['design'], False)
+ self.assertEquals(sorted([c.name for c in self.linter._checkers.values()
+ if c.is_enabled()]),
+ ['basic', 'classes', 'exceptions', 'format', 'imports',
+ 'master', 'metrics', 'miscellaneous', 'newstyle',
+ 'similarities', 'typecheck', 'variables'])
+
+ def test_enable_checkers2(self):
+ self.linter.enable_checkers(['design'], True)
+ self.assertEquals(sorted([c.name for c in self.linter._checkers.values()
+ if c.is_enabled()]),
+ ['design', 'master'])
+
+from pylint import config
+
+class ConfigTC(TestCase):
+
+ def setUp(self):
+ os.environ.pop('PYLINTRC', None)
+
+ def test_pylint_home(self):
+ uhome = os.path.expanduser('~')
+ if uhome == '~':
+ expected = '.pylint.d'
+ else:
+ expected = os.path.join(uhome, '.pylint.d')
+ self.assertEquals(config.PYLINT_HOME, expected)
+
+ try:
+ pylintd = join(tempfile.gettempdir(), '.pylint.d')
+ os.environ['PYLINTHOME'] = pylintd
+ try:
+ reload(config)
+ self.assertEquals(config.PYLINT_HOME, pylintd)
+ finally:
+ try:
+ os.remove(pylintd)
+ except:
+ pass
+ finally:
+ del os.environ['PYLINTHOME']
+
+ def test_pylintrc(self):
+ try:
+ self.assertEquals(config.find_pylintrc(), None)
+ os.environ['PYLINTRC'] = join(tempfile.gettempdir(), '.pylintrc')
+ self.assertEquals(config.find_pylintrc(), None)
+ os.environ['PYLINTRC'] = '.'
+ self.assertEquals(config.find_pylintrc(), None)
+ finally:
+ os.environ.pop('PYLINTRC', '')
+ reload(config)
+
+ def test_pylintrc_parentdir(self):
+ chroot = tempfile.mkdtemp()
+ try:
+ create_files(['a/pylintrc', 'a/b/__init__.py', 'a/b/pylintrc',
+ 'a/b/c/__init__.py', 'a/b/c/d/__init__.py'], chroot)
+ os.chdir(chroot)
+ self.assertEquals(config.find_pylintrc(), None)
+ results = {'a' : join(chroot, 'a', 'pylintrc'),
+ 'a/b' : join(chroot, 'a', 'b', 'pylintrc'),
+ 'a/b/c' : join(chroot, 'a', 'b', 'pylintrc'),
+ 'a/b/c/d' : join(chroot, 'a', 'b', 'pylintrc'),
+ }
+ for basedir, expected in results.items():
+ os.chdir(join(chroot, basedir))
+ self.assertEquals(config.find_pylintrc(), expected)
+ finally:
+ os.chdir(HERE)
+ shutil.rmtree(chroot)
+
+
+ def test_pylintrc_parentdir_no_package(self):
+ chroot = tempfile.mkdtemp()
+ try:
+ create_files(['a/pylintrc', 'a/b/pylintrc', 'a/b/c/d/__init__.py'], chroot)
+ os.chdir(chroot)
+ self.assertEquals(config.find_pylintrc(), None)
+ results = {'a' : join(chroot, 'a', 'pylintrc'),
+ 'a/b' : join(chroot, 'a', 'b', 'pylintrc'),
+ 'a/b/c' : None,
+ 'a/b/c/d' : None,
+ }
+ for basedir, expected in results.items():
+ os.chdir(join(chroot, basedir))
+ self.assertEquals(config.find_pylintrc(), expected)
+ finally:
+ os.chdir(HERE)
+ shutil.rmtree(chroot)
+
+if __name__ == '__main__':
+ unittest_main()
diff --git a/utils.py b/utils.py
new file mode 100644
index 0000000..022807c
--- /dev/null
+++ b/utils.py
@@ -0,0 +1,387 @@
+# Copyright (c) 2003-2008 Sylvain Thenault (thenault@gmail.com).
+# Copyright (c) 2003-2008 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.,
+# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+"""some various utilities and helper classes, most of them used in the
+main pylint class
+"""
+
+import sys
+from os import linesep
+
+from logilab.common.textutils import normalize_text
+from logilab.common.configuration import rest_format_section
+from logilab.common.ureports import Section
+
+from logilab.astng import Module
+
+from pylint.checkers import EmptyReport
+
+class UnknownMessage(Exception):
+ """raised when a unregistered message id is encountered"""
+
+
+MSG_TYPES = {
+ 'I' : 'info',
+ 'C' : 'convention',
+ 'R' : 'refactor',
+ 'W' : 'warning',
+ 'E' : 'error',
+ 'F' : 'fatal'
+ }
+MSG_CATEGORIES = MSG_TYPES.keys()
+
+
+def sort_checkers(checkers, enabled_only=True):
+ """return a list of enabled checker sorted by priority"""
+ if enabled_only:
+ checkers = [(-checker.priority, checker) for checker in checkers
+ if checker.is_enabled()]
+ else:
+ checkers = [(-checker.priority, checker) for checker in checkers]
+ checkers.sort()
+ return [item[1] for item in checkers]
+
+def sort_msgs(msg_ids):
+ """sort message identifiers according to their category first"""
+ msg_order = ['E', 'W', 'R', 'C', 'I', 'F']
+ def cmp_func(msgid1, msgid2):
+ """comparison function for two message identifiers"""
+ if msgid1[0] != msgid2[0]:
+ return cmp(msg_order.index(msgid1[0]), msg_order.index(msgid2[0]))
+ else:
+ return cmp(msgid1, msgid2)
+ msg_ids.sort(cmp_func)
+ return msg_ids
+
+def get_module_and_frameid(node):
+ """return the module name and the frame id in the module"""
+ frame = node.frame()
+ module, obj = '', []
+ while frame:
+ if isinstance(frame, Module):
+ module = frame.name
+ else:
+ obj.append(getattr(frame, 'name', '<lambda>'))
+ try:
+ frame = frame.parent.frame()
+ except AttributeError:
+ frame = None
+ obj.reverse()
+ return module, '.'.join(obj)
+
+
+class Message:
+ def __init__(self, checker, msgid, msg, descr):
+ assert len(msgid) == 5, 'Invalid message id %s' % msgid
+ assert msgid[0] in MSG_CATEGORIES, \
+ 'Bad message type %s in %r' % (msgid[0], msgid)
+ self.msgid = msgid
+ self.msg = msg
+ self.descr = descr
+ self.checker = checker
+
+class MessagesHandlerMixIn:
+ """a mix-in class containing all the messages related methods for the main
+ lint class
+ """
+
+ def __init__(self):
+ # dictionary of registered messages
+ self._messages = {}
+ self._msgs_state = {}
+ self._module_msgs_state = {} # None
+ self._msg_cats_state = {}
+ self._module_msg_cats_state = None
+
+ def register_messages(self, checker):
+ """register a dictionary of messages
+
+ Keys are message ids, values are a 2-uple with the message type and the
+ message itself
+
+ message ids should be a string of len 4, where the to first characters
+ are the checker id and the two last the message id in this checker
+ """
+ msgs_dict = checker.msgs
+ chkid = None
+ for msgid, (msg, msgdescr) in msgs_dict.items():
+ # avoid duplicate / malformed ids
+ assert not self._messages.has_key(msgid), \
+ 'Message id %r is already defined' % msgid
+ assert chkid is None or chkid == msgid[1:3], \
+ 'Inconsistent checker part in message id %r' % msgid
+ chkid = msgid[1:3]
+ self._messages[msgid] = Message(checker, msgid, msg, msgdescr)
+
+ def get_message_help(self, msg_id, checkerref=False):
+ """return the help string for the given message id"""
+ msg = self.check_message_id(msg_id)
+ desc = normalize_text(' '.join(msg.descr.split()), indent=' ')
+ if checkerref:
+ desc += ' This message belongs to the %s checker.' % \
+ msg.checker.name
+ title = msg.msg
+ if title != '%s':
+ title = title.splitlines()[0]
+ return ':%s: *%s*\n%s' % (msg.msgid, title, desc)
+ return ':%s:\n%s' % (msg.msgid, desc)
+
+ def disable_message(self, msg_id, scope='package', line=None):
+ """don't output message of the given id"""
+ assert scope in ('package', 'module')
+ msg = self.check_message_id(msg_id)
+ if scope == 'module':
+ assert line > 0
+ try:
+ self._module_msgs_state[msg.msgid][line] = False
+ except KeyError:
+ self._module_msgs_state[msg.msgid] = {line: False}
+ if msg_id != 'I0011':
+ self.add_message('I0011', line=line, args=msg.msgid)
+
+ else:
+ msgs = self._msgs_state
+ msgs[msg.msgid] = False
+ # sync configuration object
+ self.config.disable_msg = [mid for mid, val in msgs.items()
+ if not val]
+
+ def enable_message(self, msg_id, scope='package', line=None):
+ """reenable message of the given id"""
+ assert scope in ('package', 'module')
+ msg = self.check_message_id(msg_id)
+ msg.checker.enabled = True # ensure the related checker is enabled
+ if scope == 'module':
+ assert line > 0
+ try:
+ self._module_msgs_state[msg.msgid][line] = True
+ except KeyError:
+ self._module_msgs_state[msg.msgid] = {line: True}
+ self.add_message('I0012', line=line, args=msg.msgid)
+ else:
+ msgs = self._msgs_state
+ msgs[msg.msgid] = True
+ # sync configuration object
+ self.config.enable_msg = [mid for mid, val in msgs.items() if val]
+
+ def disable_message_category(self, msg_cat_id, scope='package', line=None):
+ """don't output message in the given category"""
+ assert scope in ('package', 'module')
+ msg_cat_id = msg_cat_id[0].upper()
+ if scope == 'module':
+ self.add_message('I0011', line=line, args=msg_cat_id)
+ self._module_msg_cats_state[msg_cat_id] = False
+ else:
+ self._msg_cats_state[msg_cat_id] = False
+
+ def enable_message_category(self, msg_cat_id, scope='package', line=None):
+ """reenable message of the given category"""
+ assert scope in ('package', 'module')
+ msg_cat_id = msg_cat_id[0].upper()
+ if scope == 'module':
+ self.add_message('I0012', line=line, args=msg_cat_id)
+ self._module_msg_cats_state[msg_cat_id] = True
+ else:
+ self._msg_cats_state[msg_cat_id] = True
+
+ def check_message_id(self, msg_id):
+ """raise UnknownMessage if the message id is not defined"""
+ msg_id = msg_id.upper()
+ try:
+ return self._messages[msg_id]
+ except KeyError:
+ raise UnknownMessage('No such message id %s' % msg_id)
+
+ def is_message_enabled(self, msg_id, line=None):
+ """return true if the message associated to the given message id is
+ enabled
+ """
+ try:
+ if not self._module_msg_cats_state[msg_id[0]]:
+ return False
+ except (KeyError, TypeError):
+ if not self._msg_cats_state.get(msg_id[0], True):
+ return False
+ if line is None:
+ return self._msgs_state.get(msg_id, True)
+ try:
+ return self._module_msgs_state[msg_id][line]
+ except (KeyError, TypeError):
+ return self._msgs_state.get(msg_id, True)
+
+ def add_message(self, msg_id, line=None, node=None, args=None):
+ """add the message corresponding to the given id.
+
+ If provided, msg is expanded using args
+
+ astng checkers should provide the node argument, raw checkers should
+ provide the line argument.
+ """
+ if line is None and node is not None:
+ line = node.fromlineno#lineno or node.statement().lineno
+ #if not isinstance(node, Module):
+ # assert line > 0, node.__class__
+ # should this message be displayed
+ if not self.is_message_enabled(msg_id, line):
+ return
+ # update stats
+ msg_cat = MSG_TYPES[msg_id[0]]
+ self.stats[msg_cat] += 1
+ self.stats['by_module'][self.current_name][msg_cat] += 1
+ try:
+ self.stats['by_msg'][msg_id] += 1
+ except KeyError:
+ self.stats['by_msg'][msg_id] = 1
+ msg = self._messages[msg_id].msg
+ # expand message ?
+ if args:
+ msg %= args
+ # get module and object
+ if node is None:
+ module, obj = self.current_name, ''
+ path = self.current_file
+ else:
+ module, obj = get_module_and_frameid(node)
+ path = node.root().file
+ # add the message
+ self.reporter.add_message(msg_id, (path, module, obj, line or 1), msg)
+
+ def help_message(self, msgids):
+ """display help messages for the given message identifiers"""
+ for msg_id in msgids:
+ try:
+ print self.get_message_help(msg_id, True)
+ print
+ except UnknownMessage, ex:
+ print ex
+ print
+ continue
+
+ def list_messages(self):
+ """output a full documentation in ReST format"""
+ for checker in sort_checkers(self._checkers.values()):
+ if checker.name == 'master':
+ prefix = 'Main '
+ if checker.options:
+ for section, options in checker.options_by_section():
+ if section is None:
+ title = 'General options'
+ else:
+ title = '%s options' % section.capitalize()
+ print title
+ print '~' * len(title)
+ rest_format_section(sys.stdout, None, options)
+ print
+ else:
+ prefix = ''
+ title = '%s checker' % checker.name.capitalize()
+ print title
+ print '-' * len(title)
+ if checker.__doc__: # __doc__ is None with -OO
+ print linesep.join([l.strip()
+ for l in checker.__doc__.splitlines()])
+ if checker.options:
+ title = 'Options'
+ print title
+ print '~' * len(title)
+ for section, options in checker.options_by_section():
+ rest_format_section(sys.stdout, section, options)
+ print
+ if checker.msgs:
+ title = ('%smessages' % prefix).capitalize()
+ print title
+ print '~' * len(title)
+ for msg_id in sort_msgs(checker.msgs.keys()):
+ print self.get_message_help(msg_id, False)
+ print
+ if getattr(checker, 'reports', None):
+ title = ('%sreports' % prefix).capitalize()
+ print title
+ print '~' * len(title)
+ for report in checker.reports:
+ print ':%s: %s' % report[:2]
+ print
+ print
+
+
+class ReportsHandlerMixIn:
+ """a mix-in class containing all the reports and stats manipulation
+ related methods for the main lint class
+ """
+ def __init__(self):
+ self._reports = {}
+ self._reports_state = {}
+
+ def register_report(self, r_id, r_title, r_cb, checker):
+ """register a report
+
+ r_id is the unique identifier for the report
+ r_title the report's title
+ r_cb the method to call to make the report
+ checker is the checker defining the report
+ """
+ r_id = r_id.upper()
+ self._reports.setdefault(checker, []).append( (r_id, r_title, r_cb) )
+
+ def enable_report(self, r_id):
+ """disable the report of the given id"""
+ r_id = r_id.upper()
+ self._reports_state[r_id] = True
+
+ def disable_report(self, r_id):
+ """disable the report of the given id"""
+ r_id = r_id.upper()
+ self._reports_state[r_id] = False
+
+ def is_report_enabled(self, r_id):
+ """return true if the report associated to the given identifier is
+ enabled
+ """
+ return self._reports_state.get(r_id, True)
+
+ def make_reports(self, stats, old_stats):
+ """render registered reports"""
+ if self.config.files_output:
+ filename = 'pylint_global.' + self.reporter.extension
+ self.reporter.set_output(open(filename, 'w'))
+ sect = Section('Report',
+ '%s statements analysed.'% (self.stats['statement']))
+ checkers = sort_checkers(self._reports.keys())
+ checkers.reverse()
+ for checker in checkers:
+ for r_id, r_title, r_cb in self._reports[checker]:
+ if not self.is_report_enabled(r_id):
+ continue
+ report_sect = Section(r_title)
+ try:
+ r_cb(report_sect, stats, old_stats)
+ except EmptyReport:
+ continue
+ report_sect.report_id = r_id
+ sect.append(report_sect)
+ self.reporter.display_results(sect)
+
+ def add_stats(self, **kwargs):
+ """add some stats entries to the statistic dictionary
+ raise an AssertionError if there is a key conflict
+ """
+ for key, value in kwargs.items():
+ if key[-1] == '_':
+ key = key[:-1]
+ assert not self.stats.has_key(key)
+ self.stats[key] = value
+ return self.stats
+