From 4f01f1114f4e11e0c6d028ee08976eebb6e81c0a Mon Sep 17 00:00:00 2001 From: AbdealiJK Date: Fri, 2 Sep 2016 18:41:01 +0530 Subject: [PATCH 01/21] requirements: Remove setuptools Pip automatically tells if setuptools is not installed when doing a source install, and on wheel installs the setuptools is not needed as the setup.py file doesn't exist anymore. Also, coala will normally be installed using wheels in pip 6.x+ because we upload wheels. Hence, we don't need setuptools. The older versions of pip are anyway not being supported because we use the ~= syntax. And the older versions of pip will just throw an import error at the setuptools. Hence, the setuptools in requirements.txt has no purpose other than forcing the user to upgrade the setuptools above the lower version we specify. Fixes https://github.com/coala-analyzer/coala-bears/issues/751 --- requirements.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 0c7b21534b..f7e7624512 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,5 @@ # Use >= for development versions so that source builds always work coala>=0.8.0 -setuptools>=19.2 munkres3==1.* pylint==1.* language-check==0.8.* From 5a4c5c26a4b8cdd8258f6116a8b3b4c8fc8f1e25 Mon Sep 17 00:00:00 2001 From: AbdealiJK Date: Fri, 2 Sep 2016 18:21:23 +0530 Subject: [PATCH 02/21] GitCommitBear: Improve assertion empty queue Earlier, if there was an error and the bear queue was not empty, the error message just showed True is not False. But now, we show the messages that were obtained as that's useful debugging infromation. --- tests/vcs/git/GitCommitBearTest.py | 34 +++++++++++++++++++----------- 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/tests/vcs/git/GitCommitBearTest.py b/tests/vcs/git/GitCommitBearTest.py index 01b279f490..57785e450b 100644 --- a/tests/vcs/git/GitCommitBearTest.py +++ b/tests/vcs/git/GitCommitBearTest.py @@ -44,6 +44,16 @@ def run_uut(self, *args, **kwargs): """ return list(result.message for result in self.uut.run(*args, **kwargs)) + def assert_no_msgs(self): + """ + Assert that there are no messages in the message queue of the bear, and + show the messages in the failure messgae if it is not empty. + """ + self.assertTrue( + self.msg_queue.empty(), + "Expected no messages in bear message queue, but got: " + + str(list(str(i) for i in self.msg_queue.queue))) + def setUp(self): self.msg_queue = Queue() self.section = Section("") @@ -100,41 +110,41 @@ def test_git_failure(self): git_error = self.msg_queue.get().message self.assertEqual(git_error[:4], "git:") - self.assertTrue(self.msg_queue.empty()) + self.assert_no_msgs() def test_empty_message(self): self.git_commit("") self.assertEqual(self.run_uut(), ["HEAD commit has no message."]) - self.assertTrue(self.msg_queue.empty()) + self.assert_no_msgs() self.assertEqual(self.run_uut(allow_empty_commit_message=True), []) - self.assertTrue(self.msg_queue.empty()) + self.assert_no_msgs() @unittest.mock.patch("bears.vcs.git.GitCommitBear.run_shell_command", return_value=("one-liner-message\n", "")) def test_pure_oneliner_message(self, patch): self.assertEqual(self.run_uut(), []) - self.assertTrue(self.msg_queue.empty()) + self.assert_no_msgs() def test_shortlog_checks_length(self): self.git_commit("Commit messages that nearly exceed default limit..") self.assertEqual(self.run_uut(), []) - self.assertTrue(self.msg_queue.empty()) + self.assert_no_msgs() self.assertEqual(self.run_uut(shortlog_length=17), ["Shortlog of HEAD commit is 33 character(s) " "longer than the limit (50 > 17)."]) - self.assertTrue(self.msg_queue.empty()) + self.assert_no_msgs() self.git_commit("Add a very long shortlog for a bad project history.") self.assertEqual(self.run_uut(), ["Shortlog of HEAD commit is 1 character(s) longer " "than the limit (51 > 50)."]) - self.assertTrue(self.msg_queue.empty()) + self.assert_no_msgs() def test_shortlog_checks_shortlog_trailing_period(self): self.git_commit("Shortlog with dot.") @@ -213,24 +223,24 @@ def test_body_checks(self): "haaaaaands") self.assertEqual(self.run_uut(), []) - self.assertTrue(self.msg_queue.empty()) + self.assert_no_msgs() self.git_commit("Shortlog only") self.assertEqual(self.run_uut(), []) - self.assertTrue(self.msg_queue.empty()) + self.assert_no_msgs() # Force a body. self.git_commit("Shortlog only ...") self.assertEqual(self.run_uut(force_body=True), ["No commit message body at HEAD."]) - self.assertTrue(self.msg_queue.empty()) + self.assert_no_msgs() # Miss a newline between shortlog and body. self.git_commit("Shortlog\nOops, body too early") self.assertEqual(self.run_uut(), ["No newline between shortlog and body at HEAD."]) - self.assertTrue(self.msg_queue.empty()) + self.assert_no_msgs() # And now too long lines. self.git_commit("Shortlog\n\n" @@ -239,7 +249,7 @@ def test_body_checks(self): "This one too, blablablablablablablablabla.") self.assertEqual(self.run_uut(body_line_length=41), ["Body of HEAD commit contains too long lines."]) - self.assertTrue(self.msg_queue.empty()) + self.assert_no_msgs() def test_different_path(self): no_git_dir = mkdtemp() From 241726c8d2a35faf3cce92bdad3b48c16e596207 Mon Sep 17 00:00:00 2001 From: AbdealiJK Date: Fri, 2 Sep 2016 18:10:27 +0530 Subject: [PATCH 03/21] GitCommitBear: Add ignore_length_regex setting ignore_length_regex should be used to ignore certain lines in a git commit message. For example, links. Closes https://github.com/coala-analyzer/coala-bears/issues/755 --- bears/vcs/git/GitCommitBear.py | 21 ++++++++++++++------- tests/vcs/git/GitCommitBearTest.py | 9 +++++++++ 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/bears/vcs/git/GitCommitBear.py b/bears/vcs/git/GitCommitBear.py index 58b24842fe..f2a1fe92e1 100644 --- a/bears/vcs/git/GitCommitBear.py +++ b/bears/vcs/git/GitCommitBear.py @@ -10,6 +10,7 @@ from coalib.results.Result import Result from coalib.results.RESULT_SEVERITY import RESULT_SEVERITY from coalib.settings.FunctionMetadata import FunctionMetadata +from coalib.settings.Setting import typed_list class GitCommitBear(GlobalBear): @@ -170,15 +171,18 @@ def check_imperative(self, paragraph): def check_body(self, body, body_line_length: int=72, - force_body: bool=False): + force_body: bool=False, + ignore_length_regex: typed_list(str)=()): """ Checks the given commit body. - :param body: The commit body splitted by lines. - :param body_line_length: The maximum line-length of the body. The - newline character at each line end does not - count to the length. - :param force_body: Whether a body shall exist or not. + :param body: The commit body splitted by lines. + :param body_line_length: The maximum line-length of the body. The + newline character at each line end does not + count to the length. + :param force_body: Whether a body shall exist or not. + :param ignore_length_regex: Lines matching each of the regular + expressions in this list will be ignored. """ if len(body) == 0: if force_body: @@ -189,5 +193,8 @@ def check_body(self, body, yield Result(self, "No newline between shortlog and body at HEAD.") return - if any(len(line) > body_line_length for line in body[1:]): + ignore_regexes = [re.compile(regex) for regex in ignore_length_regex] + if any((len(line) > body_line_length and + not any(regex.search(line) for regex in ignore_regexes)) + for line in body[1:]): yield Result(self, "Body of HEAD commit contains too long lines.") diff --git a/tests/vcs/git/GitCommitBearTest.py b/tests/vcs/git/GitCommitBearTest.py index 57785e450b..9d4082a79c 100644 --- a/tests/vcs/git/GitCommitBearTest.py +++ b/tests/vcs/git/GitCommitBearTest.py @@ -251,6 +251,15 @@ def test_body_checks(self): ["Body of HEAD commit contains too long lines."]) self.assert_no_msgs() + # Allow long lines with ignore regex + self.git_commit("Shortlog\n\n" + "This line is ok.\n" + "This line is by far too long (in this case).") + self.assertEqual(self.run_uut(body_line_length=41, + ignore_length_regex=("^.*too long",)), + []) + self.assertTrue(self.msg_queue.empty()) + def test_different_path(self): no_git_dir = mkdtemp() self.git_commit("Add a very long shortlog for a bad project history.") From 4e5a4d8821488354c4f0025b80628e3eb636ae70 Mon Sep 17 00:00:00 2001 From: AbdealiJK Date: Sat, 3 Sep 2016 17:05:47 +0530 Subject: [PATCH 04/21] LocalBearTestHelper: Return results for checking Sometimes, the check_validity() function can be used to get results and then do additional checks on the results. It's useful to give out the results in such cases. --- tests/LocalBearTestHelper.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/LocalBearTestHelper.py b/tests/LocalBearTestHelper.py index 77d75b0c9d..9dcfd8b9bb 100644 --- a/tests/LocalBearTestHelper.py +++ b/tests/LocalBearTestHelper.py @@ -89,6 +89,7 @@ def check_validity(self, msg = ("The local bear '{}' yields no result although it " "should.".format(local_bear.__class__.__name__)) self.assertNotEqual(len(bear_output), 0, msg=msg) + return bear_output def check_results(self, local_bear, From dc3d2ecade399835a105a655adf0f8a148502d87 Mon Sep 17 00:00:00 2001 From: AbdealiJK Date: Fri, 2 Sep 2016 19:10:37 +0530 Subject: [PATCH 05/21] YapfBear: Catch parse errors and give result The YapfBear earlier raised an error when the file was not parseable. Now, that error is caught and a Result is given out mentioning that the file has syntax errors and cannot be parsed. Fixes https://github.com/coala-analyzer/coala-bears/issues/750 --- bears/python/YapfBear.py | 21 ++++++++++++++++----- tests/python/YapfBearTest.py | 17 +++++++++++++++++ 2 files changed, 33 insertions(+), 5 deletions(-) diff --git a/bears/python/YapfBear.py b/bears/python/YapfBear.py index bdde5b0c44..e38991b50f 100644 --- a/bears/python/YapfBear.py +++ b/bears/python/YapfBear.py @@ -144,11 +144,22 @@ def run(self, filename, file, options += 'use_tabs = ' + str(not use_spaces) options = options.format(**locals()) - with prepare_file(options.splitlines(keepends=True), - None) as (file_, fname): - corrected = FormatFile(filename, - style_config=fname, - verify=False)[0].splitlines(True) + try: + with prepare_file(options.splitlines(keepends=True), + None) as (file_, fname): + corrected = FormatFile(filename, + style_config=fname, + verify=False)[0].splitlines(True) + except SyntaxError as err: + if isinstance(err, IndentationError): + error_type = "indentation errors (" + err.args[0] + ')' + else: + error_type = "syntax errors" + yield Result.from_values( + self, + "The code cannot be parsed due to {0}.".format(error_type), + filename, line=err.lineno, column=err.offset) + return diffs = Diff.from_string_arrays(file, corrected).split_diff() for diff in diffs: yield Result(self, diff --git a/tests/python/YapfBearTest.py b/tests/python/YapfBearTest.py index 0b967e1f7c..ccdc88bf41 100644 --- a/tests/python/YapfBearTest.py +++ b/tests/python/YapfBearTest.py @@ -31,6 +31,23 @@ def test_eof_handling(self): self.check_validity(self.uut, ['a = 2'], valid=True) self.check_validity(self.uut, ['\n'], valid=True) + def test_invalid_python(self): + results = self.check_validity( + self.uut, ['def a():', ' b=1', ' bad indent'], valid=False) + self.assertEqual(len(results), 1, str(results)) + self.assertIn('unexpected indent', results[0].message) + + results = self.check_validity( + self.uut, ['def a():', ' b=1', '\ttab error'], valid=False) + self.assertEqual(len(results), 1, str(results)) + self.assertIn('inconsistent use of tabs and spaces in indentation', + results[0].message) + + results = self.check_validity( + self.uut, ['def a(:', ' b=1', '\ttab error'], valid=False) + self.assertEqual(len(results), 1, str(results)) + self.assertIn('syntax errors', results[0].message) + def test_valid_python_2(self): self.check_validity(self.uut, ['print 1\n'], valid=True) From ae85d9d0e4b785b9ac2a19059c76f2f36e9b53f7 Mon Sep 17 00:00:00 2001 From: AbdealiJK Date: Fri, 2 Sep 2016 19:33:02 +0530 Subject: [PATCH 06/21] YapfBear: Add prefer line break setting Add prefer_line_break_after_opening_bracket which sets the yapf setting split_penalty_after_opening_bracket to an appropriate value. Closes https://github.com/coala-analyzer/coala-bears/issues/737 --- bears/python/YapfBear.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/bears/python/YapfBear.py b/bears/python/YapfBear.py index e38991b50f..03d686596b 100644 --- a/bears/python/YapfBear.py +++ b/bears/python/YapfBear.py @@ -44,7 +44,8 @@ def run(self, filename, file, split_before_logical_operator: bool=False, split_before_named_assigns: bool=True, use_spaces: bool=True, - based_on_style: str='pep8'): + based_on_style: str='pep8', + prefer_line_break_after_opening_bracket: bool=True): """ :param max_line_length: Maximum number of characters for a line. @@ -112,6 +113,9 @@ def run(self, filename, file, Uses spaces for indentation. :param based_on_style: The formatting style to be used as reference. + :param prefer_line_break_after_opening_bracket: + If True, splitting right after a open bracket will not be + preferred. """ if not file: # Yapf cannot handle zero-byte files well, and adds a redundent @@ -141,7 +145,10 @@ def run(self, filename, file, space_between_ending_comma_and_closing_bracket= \ {space_between_ending_comma_and_closing_bracket} """ - options += 'use_tabs = ' + str(not use_spaces) + options += 'use_tabs = ' + str(not use_spaces) + "\n" + options += ('split_penalty_after_opening_bracket = ' + + ('30' if prefer_line_break_after_opening_bracket + else '0') + "\n") options = options.format(**locals()) try: From c24be5931feb348cc84cbf6d6cca792e098a7110 Mon Sep 17 00:00:00 2001 From: AbdealiJK Date: Sat, 3 Sep 2016 15:19:06 +0530 Subject: [PATCH 07/21] ESLintBear: Update version of eslint Updates the version of eslint. Part of the set of commits to update all npm packages. Closes https://github.com/coala-analyzer/coala-bears/issues/779 --- bears/js/ESLintBear.py | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bears/js/ESLintBear.py b/bears/js/ESLintBear.py index a8075e6df2..7620126045 100644 --- a/bears/js/ESLintBear.py +++ b/bears/js/ESLintBear.py @@ -17,7 +17,7 @@ class ESLintBear: """ LANGUAGES = {"JavaScript", "JSX"} - REQUIREMENTS = {NpmRequirement('eslint', '2')} + REQUIREMENTS = {NpmRequirement('eslint', '3')} AUTHORS = {'The coala developers'} AUTHORS_EMAILS = {'coala-devel@googlegroups.com'} LICENSE = 'AGPL-3.0' diff --git a/package.json b/package.json index 3d827f4387..fb43e7fca0 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "complexity-report": "~2.0.0-alpha", "csslint": "~0", "dockerfile_lint": "~0", - "eslint": "~2", + "eslint": "~3", "happiness":"~7.1.2", "jshint": "~2", "postcss-cli": "~2", From 6f685f41d90ba9347578533401e4ee295a07d15a Mon Sep 17 00:00:00 2001 From: AbdealiJK Date: Sat, 3 Sep 2016 15:15:30 +0530 Subject: [PATCH 08/21] MarkdownBear: Update version of remark Now remark has moved the CLI part into a separate package remark-cli and the core is housed in remark. Hence, we use remark-cli now. Part of the set of commits to update all npm packages. --- bears/markdown/MarkdownBear.py | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bears/markdown/MarkdownBear.py b/bears/markdown/MarkdownBear.py index e770ddd15f..a54b93ac8d 100644 --- a/bears/markdown/MarkdownBear.py +++ b/bears/markdown/MarkdownBear.py @@ -17,7 +17,7 @@ class MarkdownBear: """ LANGUAGES = {"Markdown"} - REQUIREMENTS = {NpmRequirement('remark', '3')} + REQUIREMENTS = {NpmRequirement('remark-cli', '2')} AUTHORS = {'The coala developers'} AUTHORS_EMAILS = {'coala-devel@googlegroups.com'} LICENSE = 'AGPL-3.0' diff --git a/package.json b/package.json index fb43e7fca0..0be9b52deb 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "happiness":"~7.1.2", "jshint": "~2", "postcss-cli": "~2", - "remark": "~3", + "remark-cli": "~2", "tslint": "~3", "ramllint": "~1.2.2", "write-good": "~0.9.1" From 138c7aa9f2ac8ecd31cc8722677012dbb12dfb70 Mon Sep 17 00:00:00 2001 From: AbdealiJK Date: Sat, 3 Sep 2016 12:27:57 +0530 Subject: [PATCH 09/21] CSSLintBear: Update csslint version `csslint` has now added the "order rules alphabetically" config by default, hence the test cases had to be changed for this. Part of the set of commits to update all npm packages. --- bears/css/CSSLintBear.py | 2 +- package.json | 2 +- tests/css/CSSLintBearTest.py | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/bears/css/CSSLintBear.py b/bears/css/CSSLintBear.py index e2b614b077..61b9dce121 100644 --- a/bears/css/CSSLintBear.py +++ b/bears/css/CSSLintBear.py @@ -13,7 +13,7 @@ class CSSLintBear: problems or inefficiencies. """ LANGUAGES = {"CSS"} - REQUIREMENTS = {NpmRequirement('csslint', '0')} + REQUIREMENTS = {NpmRequirement('csslint', '1')} AUTHORS = {'The coala developers'} AUTHORS_EMAILS = {'coala-devel@googlegroups.com'} LICENSE = 'AGPL-3.0' diff --git a/package.json b/package.json index 0be9b52deb..4a73aa1e64 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "bootlint": "~0", "coffeelint": "~1", "complexity-report": "~2.0.0-alpha", - "csslint": "~0", + "csslint": "~1", "dockerfile_lint": "~0", "eslint": "~3", "happiness":"~7.1.2", diff --git a/tests/css/CSSLintBearTest.py b/tests/css/CSSLintBearTest.py index 70cf6c53f9..7c710461d5 100644 --- a/tests/css/CSSLintBearTest.py +++ b/tests/css/CSSLintBearTest.py @@ -3,16 +3,16 @@ good_file = """ .class { - font-weight: 400; font-size: 5px; + font-weight: 400; } """ bad_file = """ .class { - font-weight: 400 - font-size: 5px; + font-size: 5px + font-weight: 400; } """ From 600ba0b38ba6109a4842e781c90ed2c270a4d906 Mon Sep 17 00:00:00 2001 From: AbdealiJK Date: Sat, 3 Sep 2016 15:21:12 +0530 Subject: [PATCH 10/21] AlexBear: Update alex version Updates the version of alex. Part of the set of commits to update all npm packages. --- bears/natural_language/AlexBear.py | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bears/natural_language/AlexBear.py b/bears/natural_language/AlexBear.py index 3842e0fe47..4740f5c344 100644 --- a/bears/natural_language/AlexBear.py +++ b/bears/natural_language/AlexBear.py @@ -13,7 +13,7 @@ class AlexBear: writing. """ LANGUAGES = {"Natural Language"} - REQUIREMENTS = {NpmRequirement('alex', '2')} + REQUIREMENTS = {NpmRequirement('alex', '3')} AUTHORS = {'The coala developers'} AUTHORS_EMAILS = {'coala-devel@googlegroups.com'} LICENSE = 'AGPL-3.0' diff --git a/package.json b/package.json index 4a73aa1e64..740a0a06f2 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "coala-bears", "version":"0.8.0", "dependencies": { - "alex": "~2", + "alex": "~3", "autoprefixer": "~6", "bootlint": "~0", "coffeelint": "~1", From f8c00cd7f9cdf985ed242ae0ae03ec851d50068c Mon Sep 17 00:00:00 2001 From: AbdealiJK Date: Sat, 3 Sep 2016 15:35:09 +0530 Subject: [PATCH 11/21] KeywordBear: Simplify config for case sensitivity It is very rare (if ever) that users would want keyword_case_sensitive, hence as we do not ave a usecase right now where it is needed, we remove it to avoid confusion for our users. Also, this makes the config much easier to name and understand. Closes https://github.com/coala-analyzer/coala-bears/issues/782 --- .coafile | 4 +++- bears/general/KeywordBear.py | 24 ++++++------------------ tests/general/KeywordBearTest.py | 11 +++-------- 3 files changed, 12 insertions(+), 27 deletions(-) diff --git a/.coafile b/.coafile index c5f6f7a747..5e226f3b84 100644 --- a/.coafile +++ b/.coafile @@ -35,5 +35,7 @@ max_lines_per_file = 1000 enabled = False bears = KeywordBear -ci_keywords, keywords_case_insensitive = \#TODO, \# TODO, \#FIXME, \# FIXME +# Note that the ci_keywords and cs_keywords are only used here because coala +# 0.8.x (current stable release) needs them. +ci_keywords, keywords = \#TODO, \# TODO, \#FIXME, \# FIXME cs_keywords = diff --git a/bears/general/KeywordBear.py b/bears/general/KeywordBear.py index 6f4aeb6d6c..74b2d585f7 100644 --- a/bears/general/KeywordBear.py +++ b/bears/general/KeywordBear.py @@ -10,34 +10,22 @@ class KeywordBear(LocalBear): LICENSE = 'AGPL-3.0' CAN_DETECT = {'Documentation'} - @deprecate_settings(keywords_case_sensitive='cs_keywords') - def run(self, - filename, - file, - keywords_case_insensitive: list, - keywords_case_sensitive: list=()): + @deprecate_settings(keywords='ci_keywords') + def run(self, filename, file, keywords: list): ''' Checks the code files for given keywords. - :param keywords_case_insensitive: + :param keywords: A list of keywords to search for (case insensitive). Usual examples are TODO and FIXME. - :param keywords_case_sensitive: - A list of keywords to search for (case sensitive). ''' results = list() - for i in range(len(keywords_case_insensitive)): - keywords_case_insensitive[i] = keywords_case_insensitive[i].lower() + for i in range(len(keywords)): + keywords[i] = keywords[i].lower() for line_number, line in enumerate(file): - for keyword in keywords_case_sensitive: - results += self.check_line_for_keyword(line, - filename, - line_number, - keyword) - - for keyword in keywords_case_insensitive: + for keyword in keywords: results += self.check_line_for_keyword(line.lower(), filename, line_number, diff --git a/tests/general/KeywordBearTest.py b/tests/general/KeywordBearTest.py index b41d98f78a..d793dd72b1 100644 --- a/tests/general/KeywordBearTest.py +++ b/tests/general/KeywordBearTest.py @@ -11,11 +11,6 @@ KeywordBearTest = verify_local_bear( KeywordBear, valid_files=(test_file,), - invalid_files=("test line FIXME", - "test line todo", - "test line warNING", - "test line ERROR"), - settings={ - "keywords_case_sensitive": "FIXME, ERROR", - "keywords_case_insensitive": "todo, warning" - }) + invalid_files=("test line todo", + "test line warNING"), + settings={"keywords": "todo, warning"}) From 35140b48f746774fc47c038b22b2488d813a5af8 Mon Sep 17 00:00:00 2001 From: AbdealiJK Date: Sat, 3 Sep 2016 15:38:25 +0530 Subject: [PATCH 12/21] KeywordBear: Conform to style guide --- bears/general/KeywordBear.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/bears/general/KeywordBear.py b/bears/general/KeywordBear.py index 74b2d585f7..9fd05617ce 100644 --- a/bears/general/KeywordBear.py +++ b/bears/general/KeywordBear.py @@ -41,10 +41,10 @@ def check_line_for_keyword(self, line, filename, line_number, keyword): message="The line contains the keyword `{}`." .format(keyword), file=filename, - line=line_number+1, - column=pos+1, - end_line=line_number+1, - end_column=pos+len(keyword)+1, + line=line_number + 1, + column=pos + 1, + end_line=line_number + 1, + end_column=pos + len(keyword) + 1, severity=RESULT_SEVERITY.INFO)] return [] From bfd61fb7a0c4456ce812a227f3b1962b2c727879 Mon Sep 17 00:00:00 2001 From: AbdealiJK Date: Sat, 3 Sep 2016 15:41:11 +0530 Subject: [PATCH 13/21] KeywordBear: Simplify and optimize using regex Earlier we looped over each keyword and over each file. We now use regex with the OR condition, so that just 1 regex parse over the whole file is needed. --- bears/general/KeywordBear.py | 43 ++++++++++++++---------------------- 1 file changed, 16 insertions(+), 27 deletions(-) diff --git a/bears/general/KeywordBear.py b/bears/general/KeywordBear.py index 9fd05617ce..d12b26b357 100644 --- a/bears/general/KeywordBear.py +++ b/bears/general/KeywordBear.py @@ -1,3 +1,5 @@ +import re + from coalib.bearlib import deprecate_settings from coalib.bears.LocalBear import LocalBear from coalib.results.Result import RESULT_SEVERITY, Result @@ -19,32 +21,19 @@ def run(self, filename, file, keywords: list): A list of keywords to search for (case insensitive). Usual examples are TODO and FIXME. ''' - results = list() - - for i in range(len(keywords)): - keywords[i] = keywords[i].lower() + keywords_regex = re.compile( + '(' + '|'.join(re.escape(key) for key in keywords) + ')', + re.IGNORECASE) for line_number, line in enumerate(file): - for keyword in keywords: - results += self.check_line_for_keyword(line.lower(), - filename, - line_number, - keyword) - - return results - - def check_line_for_keyword(self, line, filename, line_number, keyword): - pos = line.find(keyword) - if pos != -1: - return [Result.from_values( - origin=self, - message="The line contains the keyword `{}`." - .format(keyword), - file=filename, - line=line_number + 1, - column=pos + 1, - end_line=line_number + 1, - end_column=pos + len(keyword) + 1, - severity=RESULT_SEVERITY.INFO)] - - return [] + for keyword in keywords_regex.finditer(line): + yield Result.from_values( + origin=self, + message="The line contains the keyword '{}'." + .format(keyword.group()), + file=filename, + line=line_number + 1, + column=keyword.start() + 1, + end_line=line_number + 1, + end_column=keyword.end() + 1, + severity=RESULT_SEVERITY.INFO) From fa70e9240283d92f64113adc571121e7cf263574 Mon Sep 17 00:00:00 2001 From: AbdealiJK Date: Sat, 3 Sep 2016 13:17:27 +0530 Subject: [PATCH 14/21] AlexBear: Check if wrong alex is installed Alex has a name clash with another tool in the haskell toolkit. Hence, we check here by reading the `--help` of `alex` and checking whether this is indeed the correct tool or not. Fixes https://github.com/coala-analyzer/coala-bears/issues/776 --- bears/natural_language/AlexBear.py | 27 ++++++++++++++++++++++++++ tests/natural_language/AlexBearTest.py | 27 ++++++++++++++++++++++++++ 2 files changed, 54 insertions(+) diff --git a/bears/natural_language/AlexBear.py b/bears/natural_language/AlexBear.py index 4740f5c344..24d815a5f5 100644 --- a/bears/natural_language/AlexBear.py +++ b/bears/natural_language/AlexBear.py @@ -1,3 +1,6 @@ +import subprocess +import sys + from coalib.bearlib.abstractions.Linter import linter from coalib.bears.requirements.NpmRequirement import NpmRequirement @@ -18,6 +21,30 @@ class AlexBear: AUTHORS_EMAILS = {'coala-devel@googlegroups.com'} LICENSE = 'AGPL-3.0' + @classmethod + def check_prerequisites(cls): + parent_prereqs = super().check_prerequisites() + if parent_prereqs is not True: # pragma: no cover + return parent_prereqs + + incorrect_pkg_msg = ( + "Please ensure that the package that has been installed is the " + "one to 'Catch insensitive, inconsiderate writing'. This can be " + "verified by running `alex --help` and seeing what it does.") + try: + output = subprocess.check_output(("alex", "--help"), + stderr=subprocess.STDOUT) + except (OSError, subprocess.CalledProcessError): + return ("The `alex` package could not be verified. " + + incorrect_pkg_msg) + else: + output = output.decode(sys.getfilesystemencoding()) + if "Catch insensitive, inconsiderate writing" in output: + return True + else: + return ("The `alex` package that's been installed seems to " + "be incorrect. " + incorrect_pkg_msg) + @staticmethod def create_arguments(filename, file, config_file): return filename, diff --git a/tests/natural_language/AlexBearTest.py b/tests/natural_language/AlexBearTest.py index eb6333d708..28dccfd027 100644 --- a/tests/natural_language/AlexBearTest.py +++ b/tests/natural_language/AlexBearTest.py @@ -1,4 +1,8 @@ +import unittest +from unittest.mock import patch + from bears.natural_language.AlexBear import AlexBear +from tests.BearTestHelper import generate_skip_decorator from tests.LocalBearTestHelper import verify_local_bear good_file = "Their network looks good." @@ -9,3 +13,26 @@ AlexBearTest = verify_local_bear(AlexBear, valid_files=(good_file,), invalid_files=(bad_file,)) + + +@generate_skip_decorator(AlexBear) +@patch('bears.natural_language.AlexBear.subprocess.check_output') +class AlexBearPrereqTest(unittest.TestCase): + + def test_unverified_alex_installed(self, check_output_mock): + check_output_mock.side_effect = OSError + self.assertIn('The `alex` package could not be verified', + AlexBear.check_prerequisites()) + + def test_wrong_alex_installed(self, check_output_mock): + check_output_mock.return_value = b'Unexpected output from package' + self.assertIn("The `alex` package that's been installed seems to " + "be incorrect", + AlexBear.check_prerequisites()) + + def test_right_alex_installed(self, check_output_mock): + check_output_mock.return_value = ( + b'Some text here\n' + b' Catch insensitive, inconsiderate writing\n' + b'Usage instructions and examples here ....') + self.assertTrue(AlexBear.check_prerequisites()) From 22cdc8a174aad4203ffdea76335b1b453b3f50c8 Mon Sep 17 00:00:00 2001 From: AbdealiJK Date: Wed, 31 Aug 2016 20:13:34 +0530 Subject: [PATCH 15/21] JSHintBear: Deprecate "use_es6_syntax" The "esnext" argument in JSHint has been deprecated in JSHint. Hence, we deprecate this from coala and set the es_version config instead. Fixes https://github.com/coala-analyzer/coala-bears/issues/740 --- bears/js/JSHintBear.py | 15 +++++++++------ tests/js/JSHintBearTest.py | 20 ++++++++++++++++++++ 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/bears/js/JSHintBear.py b/bears/js/JSHintBear.py index 60ae485649..2816c52b28 100644 --- a/bears/js/JSHintBear.py +++ b/bears/js/JSHintBear.py @@ -42,7 +42,8 @@ class JSHintBear: CAN_DETECT = {'Formatting', 'Syntax', 'Complexity', 'Unused Code'} @staticmethod - @deprecate_settings(cyclomatic_complexity='maxcomplexity', + @deprecate_settings(es_version='use_es6_syntax', + cyclomatic_complexity='maxcomplexity', allow_unused_variables=('prohibit_unused', negate), max_parameters='maxparams', allow_missing_semicolon='allow_missing_semicol', @@ -102,7 +103,6 @@ def generate_config(filename, file, allow_last_semicolon: bool=False, allow_func_in_loop: bool=False, allow_expr_in_assignments: bool=False, - use_es6_syntax: bool=False, use_es3_array: bool=False, environment_mootools: bool=False, environment_couch: bool=False, @@ -132,7 +132,7 @@ def generate_config(filename, file, allow_variable_shadowing: bool_or_str=False, allow_unused_variables: bool_or_str=False, allow_latedef: bool_or_str=False, - es_version: int=5, + es_version: bool_or_int=5, jshint_config: str=""): """ :param allow_bitwise_operators: @@ -225,8 +225,6 @@ def generate_config(filename, file, :param use_es3_array: This option tells JSHintBear ES3 array elision elements, or empty elements are used. - :param use_es3_array: - This option tells JSHint ECMAScript 6 specific syntax is used. :param environment_mootools: This option defines globals exposed by the Mootools. :param environment_couch: @@ -308,6 +306,12 @@ def generate_config(filename, file, This option is used to specify the ECMAScript version to which the code must adhere to. """ + # Assume that when es_version is bool, it is intended for the + # deprecated use_es6_version + if es_version is True: + es_version = 6 + elif es_version is False: + es_version = 5 if not jshint_config: options = {"bitwise": not allow_bitwise_operators, "freeze": not allow_prototype_overwrite, @@ -342,7 +346,6 @@ def generate_config(filename, file, "lastsemic": allow_last_semicolon, "loopfunc": allow_func_in_loop, "expr": allow_expr_in_assignments, - "esnext": use_es6_syntax, "elision": use_es3_array, "mootools": environment_mootools, "couch": environment_couch, diff --git a/tests/js/JSHintBearTest.py b/tests/js/JSHintBearTest.py index 1e95c996a7..7d2e53c261 100644 --- a/tests/js/JSHintBearTest.py +++ b/tests/js/JSHintBearTest.py @@ -21,6 +21,14 @@ }()); """ +test_es6 = """ +var foo = { + bar: 1, + baz: 2 +}; +var { bar, baz } = foo; +console.log(bar, baz); +""" jshintconfig = os.path.join(os.path.dirname(__file__), "test_files", @@ -55,3 +63,15 @@ invalid_files=(), valid_files=(test_file3, ), settings=settings) + +JSHintBearDeprecationTest = verify_local_bear( + JSHintBear, + valid_files=(), + invalid_files=(test_es6,), + settings={"use_es6_syntax": 'False'}) + +JSHintBearDeprecation2Test = verify_local_bear( + JSHintBear, + valid_files=(test_es6,), + invalid_files=(), + settings={"use_es6_syntax": 'True'}) From db8a2de54da6d82cecd6557602c6b57153f43884 Mon Sep 17 00:00:00 2001 From: AbdealiJK Date: Wed, 31 Aug 2016 20:48:04 +0530 Subject: [PATCH 16/21] JSHintBear: Deprecate - "globalstrict" -> "strict" The `strict` parameter has been created rather than the `globalstrict` which was used earlier. Deprecate this in the bear also. Fixes https://github.com/coala-analyzer/coala-bears/issues/270 --- bears/js/JSHintBear.py | 18 +++++++++++++----- tests/js/JSHintBearTest.py | 17 +++++++++++------ 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/bears/js/JSHintBear.py b/bears/js/JSHintBear.py index 2816c52b28..1c0e75209c 100644 --- a/bears/js/JSHintBear.py +++ b/bears/js/JSHintBear.py @@ -43,6 +43,9 @@ class JSHintBear: @staticmethod @deprecate_settings(es_version='use_es6_syntax', + javascript_strictness=( + "allow_global_strict", + lambda x: "global" if x else True), cyclomatic_complexity='maxcomplexity', allow_unused_variables=('prohibit_unused', negate), max_parameters='maxparams', @@ -90,7 +93,6 @@ def generate_config(filename, file, allow_debugger: bool=False, allow_assignment_comparisions: bool=False, allow_eval: bool=False, - allow_global_strict: bool=False, allow_increment: bool=False, allow_proto: bool=False, allow_scripturls: bool=False, @@ -98,6 +100,7 @@ def generate_config(filename, file, allow_this_statements: bool=False, allow_with_statements: bool=False, use_mozilla_extension: bool=False, + javascript_strictness: bool_or_str=True, allow_noyield: bool=False, allow_eqnull: bool=False, allow_last_semicolon: bool=False, @@ -184,9 +187,6 @@ def generate_config(filename, file, :param allow_eval: This options suppresses warnings about the use of ``eval`` function. - :param allow_global_strict: - This option suppresses warnings about the use of global strict - mode. :param allow_increment: This option suppresses warnings about the use of unary increment and decrement operators. @@ -209,6 +209,14 @@ def generate_config(filename, file, :param use_mozilla_extension: This options tells JSHint that your code uses Mozilla JavaScript extensions. + :param javascript_strictness: + Determines what sort of strictness to use in the JavaScript code. + The possible options are: + + - "global" - there must be a ``"use strict";`` at global level + - "implied" - lint the code as if there is a ``"use strict";`` + - "False" - disable warnings about strict mode + - "True" - there must be a ``"use strict";`` at function level :param allow_noyield: This option suppresses warnings about generator functions with no ``yield`` statement in them. @@ -333,7 +341,7 @@ def generate_config(filename, file, "debug": allow_debugger, "boss": allow_assignment_comparisions, "evil": allow_eval, - "globalstrict": allow_global_strict, + "strict": javascript_strictness, "plusplus": allow_increment, "proto": allow_proto, "scripturl": allow_scripturls, diff --git a/tests/js/JSHintBearTest.py b/tests/js/JSHintBearTest.py index 7d2e53c261..85a3b4df7a 100644 --- a/tests/js/JSHintBearTest.py +++ b/tests/js/JSHintBearTest.py @@ -21,7 +21,10 @@ }()); """ -test_es6 = """ +# Test strictness and ES6 +test_file4 = """ +"use strict"; + var foo = { bar: 1, baz: 2 @@ -30,6 +33,7 @@ console.log(bar, baz); """ + jshintconfig = os.path.join(os.path.dirname(__file__), "test_files", "jshintconfig.json") @@ -42,7 +46,8 @@ "shadow": "False", "allow_last_semicolon": "True", "es_version": 3, - "allow_latedef": "no_func"} + "allow_latedef": "no_func", + "javascript_strictness": "False"} JSHintBearTest = verify_local_bear(JSHintBear, @@ -67,11 +72,11 @@ JSHintBearDeprecationTest = verify_local_bear( JSHintBear, valid_files=(), - invalid_files=(test_es6,), - settings={"use_es6_syntax": 'False'}) + invalid_files=(test_file4,), + settings={"use_es6_syntax": 'False', "allow_global_strict": 'False'}) JSHintBearDeprecation2Test = verify_local_bear( JSHintBear, - valid_files=(test_es6,), + valid_files=(test_file4,), invalid_files=(), - settings={"use_es6_syntax": 'True'}) + settings={"use_es6_syntax": 'True', "allow_global_strict": 'True'}) From ee34cdcde941bb38b0743c4acdb691e009927a50 Mon Sep 17 00:00:00 2001 From: AbdealiJK Date: Sat, 3 Sep 2016 12:39:27 +0530 Subject: [PATCH 17/21] bears.configfile: Add new bear - PuppetLintBear The PuppetLintBear runs the `puppet-lint` utility on files give by coala and gives results based on this. Closes https://github.com/coala-analyzer/coala-bears/issues/46 --- Gemfile | 1 + bears/configfiles/PuppetLintBear.py | 26 +++++++++++++++++++++++++ tests/configfiles/PuppetLintBearTest.py | 17 ++++++++++++++++ 3 files changed, 44 insertions(+) create mode 100644 bears/configfiles/PuppetLintBear.py create mode 100644 tests/configfiles/PuppetLintBearTest.py diff --git a/Gemfile b/Gemfile index bacf1ba3ed..48d3cb7d45 100644 --- a/Gemfile +++ b/Gemfile @@ -5,3 +5,4 @@ gem "rubocop" gem "sqlint" gem 'scss_lint', require: false# require flag is necessary https://github.com/brigade/scss-lint#installation gem "reek" +gem "puppet-lint" diff --git a/bears/configfiles/PuppetLintBear.py b/bears/configfiles/PuppetLintBear.py new file mode 100644 index 0000000000..86a4210c2d --- /dev/null +++ b/bears/configfiles/PuppetLintBear.py @@ -0,0 +1,26 @@ +from coalib.bearlib.abstractions.Linter import linter +from coalib.bears.requirements.GemRequirement import GemRequirement + + +@linter(executable='puppet-lint', + output_format='regex', + output_regex=r'(?P\d+):(?P\d+):' + r'(?Pwarning|error):(?P.+)') +class PuppetLintBear: + ''' + Check and correct puppet configuration files using ``puppet-lint``. + + See for details about the tool. + ''' + + LANGUAGES = {"Puppet"} + REQUIREMENTS = {GemRequirement('puppet-lint', '2')} + AUTHORS = {'The coala developers'} + AUTHORS_EMAILS = {'coala-devel@googlegroups.com'} + LICENSE = 'AGPL-3.0' + CAN_FIX = {'Syntax'} + + @staticmethod + def create_arguments(filename, file, config_file): + return ('--log-format', "%{line}:%{column}:%{kind}:%{message}", + filename) diff --git a/tests/configfiles/PuppetLintBearTest.py b/tests/configfiles/PuppetLintBearTest.py new file mode 100644 index 0000000000..3776986271 --- /dev/null +++ b/tests/configfiles/PuppetLintBearTest.py @@ -0,0 +1,17 @@ +from bears.configfiles.PuppetLintBear import PuppetLintBear +from tests.LocalBearTestHelper import verify_local_bear + +good_file = """ +file { '/some.conf': + ensure => present, +} +""" + +bad_file = """ +# foo +class test::foo { } +""" + +PuppetLintBearTest = verify_local_bear(PuppetLintBear, + valid_files=(good_file,), + invalid_files=(bad_file,)) From c60e9f5eb181918eabfb13b3c2b68bffb7d41db5 Mon Sep 17 00:00:00 2001 From: AbdealiJK Date: Fri, 2 Sep 2016 20:31:42 +0530 Subject: [PATCH 18/21] ESLintBear: Handle corner case if eslint fails If eslint fails to run, for example if it gets an invalid config file it does not know how to handle, earlier the bear would throw a JSON Decoding error because it wouldn't know how to parse "" (an empty string). Now this case is handled, and it gracefull doesn't try to create results in such cases. Fixes https://github.com/coala-analyzer/coala-bears/issues/727 --- bears/js/ESLintBear.py | 2 +- tests/js/ESLintBearTest.py | 9 +++++++++ tests/js/test_files/eslintconfig_badplugin.json | 14 ++++++++++++++ 3 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 tests/js/test_files/eslintconfig_badplugin.json diff --git a/bears/js/ESLintBear.py b/bears/js/ESLintBear.py index 7620126045..d5b7a2273e 100644 --- a/bears/js/ESLintBear.py +++ b/bears/js/ESLintBear.py @@ -48,7 +48,7 @@ def generate_config(filename, file): return '{"extends": "eslint:recommended"}' def process_output(self, output, filename, file): - if not file: + if not file or not output: return output = json.loads(output) diff --git a/tests/js/ESLintBearTest.py b/tests/js/ESLintBearTest.py index bd4766a760..2bacf4901b 100644 --- a/tests/js/ESLintBearTest.py +++ b/tests/js/ESLintBearTest.py @@ -38,3 +38,12 @@ ESLintBear, valid_files=(test_good, ''), invalid_files=(test_syntax_error, test_bad)) + +# If there is an invalid config file, the results cannot be found. So, no +# file gives a result. +ESLintBearWithUnloadablePluginTest = verify_local_bear( + ESLintBear, + valid_files=(test_bad, test_good), + invalid_files=(), + settings={"eslint_config": os.path.join(test_dir, + "eslintconfig_badplugin.json")}) diff --git a/tests/js/test_files/eslintconfig_badplugin.json b/tests/js/test_files/eslintconfig_badplugin.json new file mode 100644 index 0000000000..868c9786c3 --- /dev/null +++ b/tests/js/test_files/eslintconfig_badplugin.json @@ -0,0 +1,14 @@ +{ + "extends": "eslint:recommended", + "plugins": ["invalid_plugin_should_throw_error"], + "rules": { + "consistent-return": 2, + "indent" : [1, 4], + "no-else-return" : 1, + "semi" : [1, "always"], + "space-unary-ops" : [2, { + "words": true, + "nonwords": true + }] + } +} From 5c72d4e1440563671c20cf45cc47270f65aceee0 Mon Sep 17 00:00:00 2001 From: AbdealiJK Date: Sat, 3 Sep 2016 18:01:12 +0530 Subject: [PATCH 19/21] requirements: Remove redundant comment abot nltk THe nltk versions were fixed a while back. THe comment is a remnant though, which can be removed now. Related to https://github.com/coala-analyzer/coala-bears/issues/509 --- requirements.txt | 2 -- 1 file changed, 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index f7e7624512..3493a56dfc 100644 --- a/requirements.txt +++ b/requirements.txt @@ -22,8 +22,6 @@ cppclean==0.9.* pydocstyle==1.* cmakelint==1.* vim-vint==0.3.* -# Stable version 3.2 is problematic in Windows. -# Use >=3.3,==3.* once nltk-3.3 is released. nltk==3.2.* appdirs==1.* pyyaml==3.* From 8a3d7fd0c1126051b9b79e933a06161c0079a298 Mon Sep 17 00:00:00 2001 From: AbdealiJK Date: Sat, 3 Sep 2016 18:06:25 +0530 Subject: [PATCH 20/21] generate_packageTest: Use correct casing Classes don't use snake casing, but rather CapWords convention according to PEP8. --- tests/generate_packageTest.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/generate_packageTest.py b/tests/generate_packageTest.py index 4b27bd950f..2d58ae20b2 100644 --- a/tests/generate_packageTest.py +++ b/tests/generate_packageTest.py @@ -9,7 +9,7 @@ create_upload_parser) -class touchTest(unittest.TestCase): +class TouchTest(unittest.TestCase): def setUp(self): if os.path.exists('TestFile.py'): @@ -24,7 +24,7 @@ def tearDown(self): os.remove('TestFile.py') -class create_file_from_templateTest(unittest.TestCase): +class CreateFileFromTemplateTest(unittest.TestCase): SUBST_FILE = os.path.join( 'tests', 'generate_package_input_files', 'substituted_file.py') @@ -42,7 +42,7 @@ def tearDown(self): os.remove(self.SUBST_FILE) -class create_file_structure_for_packagesTest(unittest.TestCase): +class CreateFileStructureForPackagesTest(unittest.TestCase): INIT_FILE_PATH = os.path.join('folder', 'Test', 'coalaTest', '__init__.py') BEAR_FILE_PATH = os.path.join('folder', 'Test', 'coalaTest', 'Test.py') @@ -57,13 +57,13 @@ def tearDown(self): shutil.rmtree('folder') -class create_upload_parserTest(unittest.TestCase): +class CreateUploadParserTest(unittest.TestCase): def test_parser(self): self.assertTrue(create_upload_parser().parse_args(['--upload']).upload) -class perform_registerTest(unittest.TestCase): +class PerformRegisterTest(unittest.TestCase): @patch('subprocess.call') def test_command(self, call_mock): @@ -74,7 +74,7 @@ def test_command(self, call_mock): 'MarkdownBear-0.8.0.dev20160623094115-py3-none-any.whl')]) -class perform_uploadTest(unittest.TestCase): +class PerformUploadTest(unittest.TestCase): @patch('subprocess.call') def test_command(self, call_mock): @@ -82,7 +82,7 @@ def test_command(self, call_mock): call_mock.assert_called_with(['twine', 'upload', './dist/*']) -class mainTest(unittest.TestCase): +class MainTest(unittest.TestCase): CSS_BEAR_SETUP_PATH = os.path.join( 'bears', 'upload', 'CSSLintBear', 'setup.py') From b3be8f36a8b138af4680ab5135fa24245cd5c6be Mon Sep 17 00:00:00 2001 From: Karan Date: Mon, 25 Jul 2016 15:34:48 +0530 Subject: [PATCH 21/21] natural_language: Add SpellCheckBear This bear uses ``scspell`` to check for spelling mistakes. Closes https://github.com/coala-analyzer/coala-bears/issues/629 --- bears/natural_language/SpellCheckBear.py | 24 ++++++++++++++++++++ requirements.txt | 1 + tests/natural_language/SpellCheckBearTest.py | 17 ++++++++++++++ 3 files changed, 42 insertions(+) create mode 100644 bears/natural_language/SpellCheckBear.py create mode 100644 tests/natural_language/SpellCheckBearTest.py diff --git a/bears/natural_language/SpellCheckBear.py b/bears/natural_language/SpellCheckBear.py new file mode 100644 index 0000000000..4313645770 --- /dev/null +++ b/bears/natural_language/SpellCheckBear.py @@ -0,0 +1,24 @@ +from coalib.bearlib.abstractions.Linter import linter +from coalib.bears.requirements.PipRequirement import PipRequirement + + +@linter(executable='scspell', + use_stderr=True, + output_format='regex', + output_regex=r'(?P.*):(?P.\d*):\s*(?P.*)') +class SpellCheckBear: + """ + Lints files to check for incorrect spellings using ``scspell``. + + See for more information. + """ + LANGUAGES = {"Natural Language"} + REQUIREMENTS = {PipRequirement('scspell3k', '2.0')} + AUTHORS = {'The coala developers'} + AUTHORS_EMAILS = {'coala-devel@googlegroups.com'} + LICENSE = 'AGPL-3.0' + CAN_DETECT = {'Spelling'} + + @staticmethod + def create_arguments(filename, file, config_file): + return '--report-only', filename diff --git a/requirements.txt b/requirements.txt index 3493a56dfc..558d442e12 100644 --- a/requirements.txt +++ b/requirements.txt @@ -28,3 +28,4 @@ pyyaml==3.* vulture==0.10.* nbformat>=4.* pyflakes==1.2.* # Although we don't need this directly, solves a dep conflict +scspell3k==2.* diff --git a/tests/natural_language/SpellCheckBearTest.py b/tests/natural_language/SpellCheckBearTest.py new file mode 100644 index 0000000000..8ab402647a --- /dev/null +++ b/tests/natural_language/SpellCheckBearTest.py @@ -0,0 +1,17 @@ +import platform +import unittest + +from bears.natural_language.SpellCheckBear import SpellCheckBear +from tests.LocalBearTestHelper import verify_local_bear + +good_file = "This is correct spelling." + +bad_file = "tihs si surly som incoreclt speling." + + +SpellCheckLintBearTest = unittest.skipIf( + platform.system() == "Windows", + "SpellCheckBear doesn't work on windows")( + verify_local_bear(SpellCheckBear, + valid_files=(good_file,), + invalid_files=(bad_file,)))