Repository: oppia/oppia

Issue:
Add lint check for continuation line
Continuation line `\` should not be used break long lines, instead braces `(...)` should be used. We need to implement a check for continuation lines and ensure that they are not used throughout the code base.
Add lint check for continuation line
Continuation line `\` should not be used break long lines, instead braces `(...)` should be used. We need to implement a check for continuation lines and ensure that they are not used throughout the code base.


Discussion:
Hi @apb7, I can take a shot at this. How would this be implemented? Is it just a new function in pre_commit_linter.py?
Hi, @dawsoneliasen you can implement this by creating a checker for pylint which would be implemented in scripts/pylint_extensions.py.
For more info about checker reference this http://pylint.pycqa.org/en/latest/how_tos/custom_checkers.html
and let me inform if you are not taking this . Thanks!
@import-keshav Thank you! I will go ahead and take it. 
Hi @dawsoneliasen, I was thinking if we could find up any existing rule instead of creating another function/checker. If not, then I think that a pylint custom checker would be the right way ahead. Thanks! 
Hi @dawsoneliasen, If you are currently not working on this, would it be fine if I take this up?
Thanks!
@ankita240796 please do take it up! I have been caught up with studying. 
Thanks @dawsoneliasen!
Hi @apb7, can you please assign this to me, Thanks!
Hi @apb7, I tried to find out if there is an existing rule for removing backslash continuation but I was unable to find any such rule. I found a rule which ensures that backslash should not be redundant between brackets and we can use a similar code to remove backslash continuation. Would it be fine to proceed with a pylint custom checker now?

Hi @apb7, I can take a shot at this. How would this be implemented? Is it just a new function in pre_commit_linter.py?
Hi, @dawsoneliasen you can implement this by creating a checker for pylint which would be implemented in scripts/pylint_extensions.py.
For more info about checker reference this http://pylint.pycqa.org/en/latest/how_tos/custom_checkers.html
and let me inform if you are not taking this . Thanks!
@import-keshav Thank you! I will go ahead and take it. 
Hi @dawsoneliasen, I was thinking if we could find up any existing rule instead of creating another function/checker. If not, then I think that a pylint custom checker would be the right way ahead. Thanks! 
Hi @dawsoneliasen, If you are currently not working on this, would it be fine if I take this up?
Thanks!
@ankita240796 please do take it up! I have been caught up with studying. 
Thanks @dawsoneliasen!
Hi @apb7, can you please assign this to me, Thanks!
Hi @apb7, I tried to find out if there is an existing rule for removing backslash continuation but I was unable to find any such rule. I found a rule which ensures that backslash should not be redundant between brackets and we can use a similar code to remove backslash continuation. Would it be fine to proceed with a pylint custom checker now?


Resolving patch:
diff --git a/scripts/pylint_extensions.py b/scripts/pylint_extensions.py
--- a/scripts/pylint_extensions.py
+++ b/scripts/pylint_extensions.py
@@ -725,6 +725,38 @@ def visit_importfrom(self, node):
                 pass
 
 
+class BackslashContinuationChecker(checkers.BaseChecker):
+    """Custom pylint checker which checks that backslash is not used
+    for continuation.
+    """
+    __implements__ = interfaces.IRawChecker
+
+    name = 'backslash-continuation'
+    priority = -1
+    msgs = {
+        'C0004': (
+            (
+                'Backslash should not be used to break continuation lines. '
+                'Use braces to break long lines.'),
+            'backslash-continuation',
+            'Use braces to break long lines instead of backslash.'
+        ),
+    }
+
+    def process_module(self, node):
+        """Process a module.
+
+        Args:
+            node: astroid.scoped_nodes.Function. Node to access module content.
+        """
+        with node.stream() as stream:
+            file_content = stream.readlines()
+            for (line_num, line) in enumerate(file_content):
+                if line.rstrip('\r\n').endswith('\\'):
+                    self.add_message(
+                        'backslash-continuation', line=line_num + 1)
+
+
 def register(linter):
     """Registers the checker with pylint.
 
@@ -735,3 +767,4 @@ def register(linter):
     linter.register_checker(HangingIndentChecker(linter))
     linter.register_checker(DocstringParameterChecker(linter))
     linter.register_checker(ImportOnlyModulesChecker(linter))
+    linter.register_checker(BackslashContinuationChecker(linter))


Test patch:
diff --git a/scripts/pylint_extensions_test.py b/scripts/pylint_extensions_test.py
--- a/scripts/pylint_extensions_test.py
+++ b/scripts/pylint_extensions_test.py
@@ -189,3 +189,44 @@ def test_finds_import_from(self):
         ):
             checker_test_object.checker.visit_importfrom(
                 importfrom_node2)
+
+
+class BackslashContinuationCheckerTests(unittest.TestCase):
+
+    def test_finds_backslash_continuation(self):
+        checker_test_object = testutils.CheckerTestCase()
+        checker_test_object.CHECKER_CLASS = (
+            pylint_extensions.BackslashContinuationChecker)
+        checker_test_object.setup_method()
+        node = astroid.scoped_nodes.Module(name='test', doc='Custom test')
+        temp_file = tempfile.NamedTemporaryFile()
+        filename = temp_file.name
+
+        with open(filename, 'w') as tmp:
+            tmp.write(
+                """message1 = 'abc'\\\n""" # pylint: disable=backslash-continuation
+                """'cde'\\\n""" # pylint: disable=backslash-continuation
+                """'xyz'
+                message2 = 'abc\\\\'
+                message3 = (
+                    'abc\\\\'
+                    'xyz\\\\'
+                )
+                """)
+
+        node.file = filename
+        node.path = filename
+
+        checker_test_object.checker.process_module(node)
+
+        with checker_test_object.assertAddsMessages(
+            testutils.Message(
+                msg_id='backslash-continuation',
+                line=1
+            ),
+            testutils.Message(
+                msg_id='backslash-continuation',
+                line=2
+            ),
+        ):
+            temp_file.close()