Repository: dmytrostriletskyi/pdbe

Issue:
Possible bug in multi-line function definition
https://github.com/dmytrostriletskyi/pdbe/blob/e103eb47de9961a1c4f7757cc37ca40eb98c0165/pdbe/utils.py#L11

This propably won't work with multiline function definitions.

Comments:

Patch:
diff --git a/.pylintrc b/.pylintrc
index 2a8fb44..5a5467d 100644
--- a/.pylintrc
+++ b/.pylintrc
@@ -304,7 +304,7 @@ function-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$
 function-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$
 
 # Good variable names which should always be accepted, separated by a comma
-good-names=i,j,k,ex,Run,_,app_name,logger,urlpatterns,application,server_port
+good-names=i,j,k,ex,Run,_,app_name,logger,urlpatterns,application,server_port,multiple_function_declaration_begging_spaces,stored_import_pdb_line_begging_spaces
 
 # Include a hint for the correct naming format with invalid-name
 include-naming-hint=no
diff --git a/pdbe/commits.py b/pdbe/commits.py
index b5e31a5..425af66 100644
--- a/pdbe/commits.py
+++ b/pdbe/commits.py
@@ -1,8 +1,9 @@
 """
 Pdbe commits logic.
 """
-from datetime import datetime
 import binascii
+import keyword
+from datetime import datetime
 from os import fdopen, getcwd, path, mkdir, walk, urandom, listdir
 from os.path import join
 from tempfile import mkstemp
@@ -24,48 +25,89 @@ def get_commit_functions(file_path: str) -> list:
     with open(file_path, 'r') as file:
         content = [line.strip() for line in file.readlines()]
 
+        suggested_function = ''
+
         # pylint:disable=consider-using-enumerate
-        for index in range(len(content)):
-            line = content[index]
+        for line in content:
 
-            if utils.does_line_contains_import_pdb(line):
-                suggested_function = content[index - 1]
+            if utils.is_one_line_function_declaration_line(line) and not utils.is_commended_function(line):
+                function_open_bracket_index = line.index('(')
+                suggested_function = line[4:function_open_bracket_index]
 
-                if utils.is_function_sign_in_line(suggested_function):
-                    function_open_bracket_index = suggested_function.index('(')
-                    function_name = suggested_function[4:function_open_bracket_index]
+            elif 'def' in line and '(' in line and not utils.is_commended_function(line):
+                function_open_bracket_index = line.index('(')
+                suggested_function = line[4:function_open_bracket_index]
 
-                    commit_functions.append(function_name)
+            if utils.does_line_contains_import_pdb(line):
+                commit_functions.append(suggested_function)
 
     return commit_functions
 
 
+# too-many-locals, too-many-branches, refactor
+# pylint:disable=too-many-locals,too-many-branches
 def restore_import_pdb_from_commit(commit_content: List[str], call_commit_path: str) -> None:
     """
     Restore import pdb statements from specified commit.
     """
-    file_to_restore = ''
+    commit_content.append('.py')  # mock for algorithm below
+
+    files_to_restore = []
+    functions_to_restore = []
+    temp_restore = []
+
+    for content in commit_content:
+
+        if '.py' not in content:
+            temp_restore.append(content)
 
-    for python_file in commit_content:
+        if '.py' in content:
+            if temp_restore:
+                functions_to_restore.append(temp_restore)
+                temp_restore = []
 
-        if '.py' in python_file:
-            file_to_restore = call_commit_path + python_file
+            file_to_restore = call_commit_path + content
+            files_to_restore.append(file_to_restore)
 
-        else:
-            fh, abs_path = mkstemp()  # pylint:disable=invalid-name
+    for file_to_restore, functions in zip(files_to_restore, functions_to_restore):
 
-            with fdopen(fh, 'w') as new_file:
-                with open(file_to_restore) as old_file:
-                    for line in old_file:
-                        new_file.write(line)
+        fh, abs_path = mkstemp()  # pylint:disable=invalid-name
 
-                        if 'def ' + python_file + '(' in line:
+        with fdopen(fh, 'w') as new_file:
+            with open(file_to_restore) as old_file:
+                stored_import_pdb_line_begging_spaces = ''
+                stored_function_name = ''
 
+                for line in old_file:
+                    keywords_in_line = list(set(keyword.kwlist).intersection(line.split()))
+                    new_file.write(line)
+
+                    if 'def ' in line and '(' in line and '):' in line and not utils.is_commended_function(line):
+
+                        strip_line = line.strip()
+                        function_open_bracket_index = strip_line.index('(')
+                        function_name = strip_line[4:function_open_bracket_index]
+
+                        if function_name in functions:
                             # pylint:disable=invalid-name
                             import_pdb_line_begging_spaces = utils.get_import_pdb_line_begging_spaces(line)
                             new_file.write(import_pdb_line_begging_spaces + utils.IMPORT_PDB_LINE)
 
-            utils.change_files_data(file_to_restore, abs_path)
+                    elif 'def ' in line and '(' in line and '):' not in line:
+                        function_name = line.split()[1][:-1]
+
+                        if function_name in functions:
+                            import_pdb_line_begging_spaces = utils.get_import_pdb_line_begging_spaces(line)
+                            stored_import_pdb_line_begging_spaces = import_pdb_line_begging_spaces
+                            stored_function_name = function_name
+                        else:
+                            stored_function_name = ''
+
+                    elif 'def' not in line and '):' in line and not keywords_in_line:
+                        if stored_function_name in functions:
+                            new_file.write(stored_import_pdb_line_begging_spaces + utils.IMPORT_PDB_LINE)
+
+        utils.change_files_data(file_to_restore, abs_path)
 
 
 def handle_commits_log() -> None:
diff --git a/pdbe/main.py b/pdbe/main.py
index a30afcd..d6d26b5 100644
--- a/pdbe/main.py
+++ b/pdbe/main.py
@@ -21,14 +21,36 @@ def put_import_pdb(file_path: str) -> None:
 
     with fdopen(fh, 'w') as new_file:
         with open(file_path) as old_file:
+
+            # for multiple function declaration
+            import_pdb_statement_to_write = ''
+            multiple_function_declaration_begging_spaces = ''
+
             for line in old_file:
-                if utils.is_function_sign_in_line(line) and not utils.is_commended_function(line):
+                if multiple_function_declaration_begging_spaces:
+                    if 'def' not in line and '):' in line:
+                        import_pdb_line_begging_spaces = multiple_function_declaration_begging_spaces
+                        import_pdb_statement_to_write = import_pdb_line_begging_spaces + utils.IMPORT_PDB_LINE
+                        multiple_function_declaration_begging_spaces = ''
+
+                if utils.is_one_line_function_declaration_line(line) and not utils.is_commended_function(line):
                     indents_space_count = utils.get_function_indent(line)
                     import_pdb_line_begging_spaces = utils.get_import_pdb_line_st_spaces(indents_space_count)
                     new_file.write(line + import_pdb_line_begging_spaces + utils.IMPORT_PDB_LINE)
+
                 else:
+                    if 'def' in line and not utils.is_commended_function(line):
+                        indents_space_count = utils.get_function_indent(line)
+                        multiple_function_declaration_begging_spaces = utils.get_import_pdb_line_st_spaces(
+                            indents_space_count
+                        )
+
                     new_file.write(line)
 
+                if import_pdb_statement_to_write:
+                    new_file.write(import_pdb_statement_to_write)
+                    import_pdb_statement_to_write = ''
+
     utils.change_files_data(file_path, abs_path)
 
 
diff --git a/pdbe/utils.py b/pdbe/utils.py
index 327aa9c..687ac76 100644
--- a/pdbe/utils.py
+++ b/pdbe/utils.py
@@ -8,7 +8,7 @@ IMPORT_PDB_LINE = 'import pdb; pdb.set_trace()\n'
 LINE_FEED = '\n'
 
 
-def is_function_sign_in_line(line: str) -> bool:
+def is_one_line_function_declaration_line(line: str) -> bool:  # pylint:disable=invalid-name
     """
     Check if line contains function declaration.
     """
@@ -19,7 +19,7 @@ def does_line_contains_import_pdb(line: str) -> bool:
     """
     Check if line contains import pdb statement.
     """
-    return 'import pdb; pdb.set_trace()' in line
+    return ['import', 'pdb;', 'pdb.set_trace()'] == line.split()
 
 
 def is_commended_function(line: str) -> bool:


Test patch:
diff --git a/tests/test_utils.py b/tests/test_utils.py
index 17480bc..bd96a5c 100644
--- a/tests/test_utils.py
+++ b/tests/test_utils.py
@@ -8,7 +8,7 @@ from ddt import data, ddt, unpack
 from pdbe.utils import (
     get_import_pdb_line_st_spaces,
     get_function_indent,
-    is_function_sign_in_line,
+    is_one_line_function_declaration_line,
 )
 
 
@@ -29,7 +29,7 @@ class TestUtils(unittest.TestCase):
         Case: needs to detect function declaration.
         Expected: only line, that contains `def`, `(` and `):` substring confirmed.
         """
-        result = is_function_sign_in_line(line)
+        result = is_one_line_function_declaration_line(line)
         self.assertEqual(expected, result)
 
     @data(