diff --git a/pr_agent/agent/pr_agent.py b/pr_agent/agent/pr_agent.py index 672a7a94..4b7af70a 100644 --- a/pr_agent/agent/pr_agent.py +++ b/pr_agent/agent/pr_agent.py @@ -1,11 +1,11 @@ import re +from pr_agent.config_loader import settings from pr_agent.tools.pr_code_suggestions import PRCodeSuggestions from pr_agent.tools.pr_description import PRDescription from pr_agent.tools.pr_information_from_user import PRInformationFromUser from pr_agent.tools.pr_questions import PRQuestions from pr_agent.tools.pr_reviewer import PRReviewer -from pr_agent.config_loader import settings class PRAgent: diff --git a/pr_agent/algo/git_patch_processing.py b/pr_agent/algo/git_patch_processing.py index 87761e1b..d8aa1802 100644 --- a/pr_agent/algo/git_patch_processing.py +++ b/pr_agent/algo/git_patch_processing.py @@ -158,7 +158,7 @@ def convert_to_hunks_with_lines_numbers(patch: str, file) -> str: patch_with_lines_str += f"{start2 + i} {line_new}\n" if old_content_lines: patch_with_lines_str += '--old hunk--\n' - for i, line_old in enumerate(old_content_lines): + for line_old in old_content_lines: patch_with_lines_str += f"{line_old}\n" new_content_lines = [] old_content_lines = [] @@ -179,7 +179,7 @@ def convert_to_hunks_with_lines_numbers(patch: str, file) -> str: patch_with_lines_str += f"{start2 + i} {line_new}\n" if old_content_lines: patch_with_lines_str += '\n--old hunk--\n' - for i, line_old in enumerate(old_content_lines): + for line_old in old_content_lines: patch_with_lines_str += f"{line_old}\n" return patch_with_lines_str.strip() diff --git a/pr_agent/algo/pr_processing.py b/pr_agent/algo/pr_processing.py index 3ea79676..a57b0e4b 100644 --- a/pr_agent/algo/pr_processing.py +++ b/pr_agent/algo/pr_processing.py @@ -4,8 +4,7 @@ import difflib import logging from typing import Any, Tuple, Union -from pr_agent.algo.git_patch_processing import extend_patch, handle_patch_deletions, \ - convert_to_hunks_with_lines_numbers +from pr_agent.algo.git_patch_processing import convert_to_hunks_with_lines_numbers, extend_patch, handle_patch_deletions from pr_agent.algo.language_handler import sort_files_by_main_languages from pr_agent.algo.token_handler import TokenHandler from pr_agent.config_loader import settings diff --git a/pr_agent/algo/utils.py b/pr_agent/algo/utils.py index 1f8c175f..f813b8cd 100644 --- a/pr_agent/algo/utils.py +++ b/pr_agent/algo/utils.py @@ -89,8 +89,8 @@ def try_fix_json(review, max_iter=10, code_suggestions=False): data = {} return data + def fix_json_escape_char(json_message=None): - result = None try: result = json.loads(json_message) except Exception as e: @@ -100,5 +100,5 @@ def fix_json_escape_char(json_message=None): json_message = list(json_message) json_message[idx_to_replace] = ' ' new_message = ''.join(json_message) - return fix_JSON(json_message=new_message) - return result \ No newline at end of file + return fix_json_escape_char(json_message=new_message) + return result diff --git a/pr_agent/git_providers/__init__.py b/pr_agent/git_providers/__init__.py index 3662fd15..646876c5 100644 --- a/pr_agent/git_providers/__init__.py +++ b/pr_agent/git_providers/__init__.py @@ -1,7 +1,7 @@ from pr_agent.config_loader import settings +from pr_agent.git_providers.bitbucket_provider import BitbucketProvider from pr_agent.git_providers.github_provider import GithubProvider from pr_agent.git_providers.gitlab_provider import GitLabProvider -from pr_agent.git_providers.bitbucket_provider import BitbucketProvider _GIT_PROVIDERS = { 'github': GithubProvider, diff --git a/pr_agent/git_providers/bitbucket_provider.py b/pr_agent/git_providers/bitbucket_provider.py index 86e445ac..bb7b2c1d 100644 --- a/pr_agent/git_providers/bitbucket_provider.py +++ b/pr_agent/git_providers/bitbucket_provider.py @@ -1,5 +1,4 @@ import logging -from datetime import datetime from typing import Optional, Tuple from urllib.parse import urlparse @@ -10,6 +9,7 @@ from pr_agent.config_loader import settings from .git_provider import FilePatchInfo + class BitbucketProvider: def __init__(self, pr_url: Optional[str] = None): s = requests.Session() @@ -45,7 +45,8 @@ class BitbucketProvider: for index, diff in enumerate(diffs): original_file_content_str = self._get_pr_file_content(diff.old.get_data('links')) new_file_content_str = self._get_pr_file_content(diff.new.get_data('links')) - diff_files.append(FilePatchInfo(original_file_content_str, new_file_content_str, diff_split[index], diff.new.path)) + diff_files.append(FilePatchInfo(original_file_content_str, new_file_content_str, + diff_split[index], diff.new.path)) return diff_files def publish_comment(self, pr_comment: str, is_temporary: bool = False): diff --git a/pr_agent/git_providers/git_provider.py b/pr_agent/git_providers/git_provider.py index 4beba204..55d2bf34 100644 --- a/pr_agent/git_providers/git_provider.py +++ b/pr_agent/git_providers/git_provider.py @@ -3,12 +3,15 @@ from dataclasses import dataclass # enum EDIT_TYPE (ADDED, DELETED, MODIFIED, RENAMED) from enum import Enum + + class EDIT_TYPE(Enum): ADDED = 1 DELETED = 2 MODIFIED = 3 RENAMED = 4 + @dataclass class FilePatchInfo: base_file: str diff --git a/pr_agent/git_providers/gitlab_provider.py b/pr_agent/git_providers/gitlab_provider.py index 0149662b..d83ae6d5 100644 --- a/pr_agent/git_providers/gitlab_provider.py +++ b/pr_agent/git_providers/gitlab_provider.py @@ -8,7 +8,7 @@ from gitlab import GitlabGetError from pr_agent.config_loader import settings -from .git_provider import FilePatchInfo, GitProvider, EDIT_TYPE +from .git_provider import EDIT_TYPE, FilePatchInfo, GitProvider class GitLabProvider(GitProvider): @@ -51,7 +51,8 @@ class GitLabProvider(GitProvider): try: return self.gl.projects.get(self.id_project).files.get(file_path, branch).decode() except GitlabGetError: - # In case of file creation the method returns GitlabGetError (404 file not found). In this case we return an empty string for the diff. + # In case of file creation the method returns GitlabGetError (404 file not found). + # In this case we return an empty string for the diff. return '' def get_diff_files(self) -> list[FilePatchInfo]: @@ -137,7 +138,8 @@ class GitLabProvider(GitProvider): lines = target_file.head_file.splitlines() relevant_line_in_file = lines[relevant_lines_start - 1] - edit_type, found, source_line_no, target_file, target_line_no = self.find_in_file(target_file, relevant_line_in_file) + edit_type, found, source_line_no, target_file, target_line_no = self.find_in_file(target_file, + relevant_line_in_file) self.send_inline_comment(body, edit_type, found, relevant_file, relevant_line_in_file, source_line_no, target_file, target_line_no) @@ -159,7 +161,7 @@ class GitLabProvider(GitProvider): target_file = file patch = file.patch patch_lines = patch.splitlines() - for i, line in enumerate(patch_lines): + for line in patch_lines: if line.startswith('@@'): match = self.RE_HUNK_HEADER.match(line) if not match: diff --git a/pr_agent/servers/github_action_runner.py b/pr_agent/servers/github_action_runner.py index 31a4800d..65643772 100644 --- a/pr_agent/servers/github_action_runner.py +++ b/pr_agent/servers/github_action_runner.py @@ -1,14 +1,9 @@ import asyncio import json import os -import re from pr_agent.agent.pr_agent import PRAgent from pr_agent.config_loader import settings -from pr_agent.tools.pr_code_suggestions import PRCodeSuggestions -from pr_agent.tools.pr_description import PRDescription -from pr_agent.tools.pr_information_from_user import PRInformationFromUser -from pr_agent.tools.pr_questions import PRQuestions from pr_agent.tools.pr_reviewer import PRReviewer diff --git a/pr_agent/servers/github_polling.py b/pr_agent/servers/github_polling.py index 4e567862..746fb361 100644 --- a/pr_agent/servers/github_polling.py +++ b/pr_agent/servers/github_polling.py @@ -1,6 +1,5 @@ import asyncio import logging -import re import sys from datetime import datetime, timezone @@ -10,10 +9,6 @@ from pr_agent.agent.pr_agent import PRAgent from pr_agent.config_loader import settings from pr_agent.git_providers import get_git_provider from pr_agent.servers.help import bot_help_text -from pr_agent.tools.pr_code_suggestions import PRCodeSuggestions -from pr_agent.tools.pr_description import PRDescription -from pr_agent.tools.pr_questions import PRQuestions -from pr_agent.tools.pr_reviewer import PRReviewer logging.basicConfig(stream=sys.stdout, level=logging.DEBUG) NOTIFICATION_URL = "https://api.github.com/notifications" @@ -103,5 +98,6 @@ async def polling_loop(): except Exception as e: logging.error(f"Exception during processing of a notification: {e}") + if __name__ == '__main__': asyncio.run(polling_loop()) diff --git a/pr_agent/tools/pr_code_suggestions.py b/pr_agent/tools/pr_code_suggestions.py index c75b3771..d8203443 100644 --- a/pr_agent/tools/pr_code_suggestions.py +++ b/pr_agent/tools/pr_code_suggestions.py @@ -8,9 +8,9 @@ from jinja2 import Environment, StrictUndefined from pr_agent.algo.ai_handler import AiHandler from pr_agent.algo.pr_processing import get_pr_diff from pr_agent.algo.token_handler import TokenHandler -from pr_agent.algo.utils import convert_to_markdown, try_fix_json +from pr_agent.algo.utils import try_fix_json from pr_agent.config_loader import settings -from pr_agent.git_providers import get_git_provider, BitbucketProvider +from pr_agent.git_providers import BitbucketProvider, get_git_provider from pr_agent.git_providers.git_provider import get_main_pr_language @@ -62,7 +62,6 @@ class PRCodeSuggestions: logging.info('Pushing inline code comments...') self.push_inline_code_suggestions(data) - async def _get_prediction(self): variables = copy.deepcopy(self.vars) variables["diff"] = self.patches_diff # update diff @@ -98,12 +97,12 @@ class PRCodeSuggestions: relevant_lines_start = int(relevant_lines_str.split('-')[0]) # absolute position relevant_lines_end = int(relevant_lines_str.split('-')[-1]) content = d['suggestion content'] - existing_code_snippet = d['existing code'] new_code_snippet = d['improved code'] if new_code_snippet: try: # dedent code snippet - self.diff_files = self.git_provider.diff_files if self.git_provider.diff_files else self.git_provider.get_diff_files() + self.diff_files = self.git_provider.diff_files if self.git_provider.diff_files \ + else self.git_provider.get_diff_files() original_initial_line = None for file in self.diff_files: if file.filename.strip() == relevant_file: @@ -121,7 +120,7 @@ class PRCodeSuggestions: logging.info(f"Could not dedent code snippet for file {relevant_file}, error: {e}") body = f"**Suggestion:** {content}\n```suggestion\n" + new_code_snippet + "\n```" - success = self.git_provider.publish_code_suggestion(body=body, + self.git_provider.publish_code_suggestion(body=body, relevant_file=relevant_file, relevant_lines_start=relevant_lines_start, relevant_lines_end=relevant_lines_end) diff --git a/pr_agent/tools/pr_description.py b/pr_agent/tools/pr_description.py index 8600b3fa..53e57a8c 100644 --- a/pr_agent/tools/pr_description.py +++ b/pr_agent/tools/pr_description.py @@ -7,7 +7,6 @@ from jinja2 import Environment, StrictUndefined from pr_agent.algo.ai_handler import AiHandler from pr_agent.algo.pr_processing import get_pr_diff from pr_agent.algo.token_handler import TokenHandler -from pr_agent.algo.utils import convert_to_markdown from pr_agent.config_loader import settings from pr_agent.git_providers import get_git_provider from pr_agent.git_providers.git_provider import get_main_pr_language diff --git a/pr_agent/tools/pr_information_from_user.py b/pr_agent/tools/pr_information_from_user.py index ff78858f..b1cdc3b8 100644 --- a/pr_agent/tools/pr_information_from_user.py +++ b/pr_agent/tools/pr_information_from_user.py @@ -67,5 +67,5 @@ class PRInformationFromUser: if settings.config.verbosity_level >= 2: logging.info(f"answer_str:\n{model_output}") answer_str = f"{model_output}\n\n Please respond to the questions above in the following format:\n\n" +\ - f"\n>/answer\n>1) ...\n>2) ...\n>...\n" + "\n>/answer\n>1) ...\n>2) ...\n>...\n" return answer_str diff --git a/pr_agent/tools/pr_reviewer.py b/pr_agent/tools/pr_reviewer.py index 0c9ab07e..ab6ffe9b 100644 --- a/pr_agent/tools/pr_reviewer.py +++ b/pr_agent/tools/pr_reviewer.py @@ -11,7 +11,7 @@ from pr_agent.algo.utils import convert_to_markdown, try_fix_json from pr_agent.config_loader import settings from pr_agent.git_providers import get_git_provider from pr_agent.git_providers.git_provider import get_main_pr_language -from pr_agent.servers.help import bot_help_text, actions_help_text +from pr_agent.servers.help import actions_help_text, bot_help_text class PRReviewer: diff --git a/tests/unit/test_fix_output.py b/tests/unit/test_fix_output.py index 08e0504b..0fdb6e3c 100644 --- a/tests/unit/test_fix_output.py +++ b/tests/unit/test_fix_output.py @@ -1,13 +1,12 @@ # Generated by CodiumAI + from pr_agent.algo.utils import try_fix_json -import pytest - class TestTryFixJson: # Tests that JSON with complete 'Code suggestions' section returns expected output def test_incomplete_code_suggestions(self): - review = '{"PR Analysis": {"Main theme": "xxx", "Type of PR": "Bug fix"}, "PR Feedback": {"General PR suggestions": "..., `xxx`...", "Code suggestions": [{"relevant file": "xxx.py", "suggestion content": "xxx [important]"}, {"suggestion number": 2, "relevant file": "yyy.py", "suggestion content": "yyy [incomp...' + review = '{"PR Analysis": {"Main theme": "xxx", "Type of PR": "Bug fix"}, "PR Feedback": {"General PR suggestions": "..., `xxx`...", "Code suggestions": [{"relevant file": "xxx.py", "suggestion content": "xxx [important]"}, {"suggestion number": 2, "relevant file": "yyy.py", "suggestion content": "yyy [incomp...' # noqa: E501 expected_output = { 'PR Analysis': { 'Main theme': 'xxx', @@ -26,7 +25,7 @@ class TestTryFixJson: assert try_fix_json(review) == expected_output def test_incomplete_code_suggestions_new_line(self): - review = '{"PR Analysis": {"Main theme": "xxx", "Type of PR": "Bug fix"}, "PR Feedback": {"General PR suggestions": "..., `xxx`...", "Code suggestions": [{"relevant file": "xxx.py", "suggestion content": "xxx [important]"} \n\t, {"suggestion number": 2, "relevant file": "yyy.py", "suggestion content": "yyy [incomp...' + review = '{"PR Analysis": {"Main theme": "xxx", "Type of PR": "Bug fix"}, "PR Feedback": {"General PR suggestions": "..., `xxx`...", "Code suggestions": [{"relevant file": "xxx.py", "suggestion content": "xxx [important]"} \n\t, {"suggestion number": 2, "relevant file": "yyy.py", "suggestion content": "yyy [incomp...' # noqa: E501 expected_output = { 'PR Analysis': { 'Main theme': 'xxx', @@ -45,7 +44,7 @@ class TestTryFixJson: assert try_fix_json(review) == expected_output def test_incomplete_code_suggestions_many_close_brackets(self): - review = '{"PR Analysis": {"Main theme": "xxx", "Type of PR": "Bug fix"}, "PR Feedback": {"General PR suggestions": "..., `xxx`...", "Code suggestions": [{"relevant file": "xxx.py", "suggestion content": "xxx [important]"} \n, {"suggestion number": 2, "relevant file": "yyy.py", "suggestion content": "yyy }, [}\n ,incomp.} ,..' + review = '{"PR Analysis": {"Main theme": "xxx", "Type of PR": "Bug fix"}, "PR Feedback": {"General PR suggestions": "..., `xxx`...", "Code suggestions": [{"relevant file": "xxx.py", "suggestion content": "xxx [important]"} \n, {"suggestion number": 2, "relevant file": "yyy.py", "suggestion content": "yyy }, [}\n ,incomp.} ,..' # noqa: E501 expected_output = { 'PR Analysis': { 'Main theme': 'xxx', @@ -64,7 +63,7 @@ class TestTryFixJson: assert try_fix_json(review) == expected_output def test_incomplete_code_suggestions_relevant_file(self): - review = '{"PR Analysis": {"Main theme": "xxx", "Type of PR": "Bug fix"}, "PR Feedback": {"General PR suggestions": "..., `xxx`...", "Code suggestions": [{"relevant file": "xxx.py", "suggestion content": "xxx [important]"}, {"suggestion number": 2, "relevant file": "yyy.p' + review = '{"PR Analysis": {"Main theme": "xxx", "Type of PR": "Bug fix"}, "PR Feedback": {"General PR suggestions": "..., `xxx`...", "Code suggestions": [{"relevant file": "xxx.py", "suggestion content": "xxx [important]"}, {"suggestion number": 2, "relevant file": "yyy.p' # noqa: E501 expected_output = { 'PR Analysis': { 'Main theme': 'xxx',