diff --git a/aider/coders/udiff_simple_prompts.py b/aider/coders/udiff_simple_prompts.py index ea98164cf..cd3160e58 100644 --- a/aider/coders/udiff_simple_prompts.py +++ b/aider/coders/udiff_simple_prompts.py @@ -22,4 +22,4 @@ Don't leave out any lines or the diff patch won't apply correctly. To make a new file, show a diff from `--- /dev/null` to `+++ path/to/new/file.ext`. {final_reminders} -""" # noqa +""" # noqa diff --git a/aider/linter.py b/aider/linter.py index 9c980366d..301e5d7aa 100644 --- a/aider/linter.py +++ b/aider/linter.py @@ -1,5 +1,4 @@ import os -import oslex import re import shlex import subprocess @@ -9,6 +8,7 @@ import warnings from dataclasses import dataclass from pathlib import Path +import oslex from grep_ast import TreeContext, filename_to_lang from grep_ast.tsl import get_parser # noqa: E402 diff --git a/aider/repo.py b/aider/repo.py index aa2d525f7..6db4c7635 100644 --- a/aider/repo.py +++ b/aider/repo.py @@ -72,7 +72,7 @@ class GitRepo: commit_prompt=None, subtree_only=False, git_commit_verify=True, - attribute_co_authored_by=False, # Added parameter + attribute_co_authored_by=False, # Added parameter ): self.io = io self.models = models @@ -84,7 +84,7 @@ class GitRepo: self.attribute_committer = attribute_committer self.attribute_commit_message_author = attribute_commit_message_author self.attribute_commit_message_committer = attribute_commit_message_committer - self.attribute_co_authored_by = attribute_co_authored_by # Assign from parameter + self.attribute_co_authored_by = attribute_co_authored_by # Assign from parameter self.commit_prompt = commit_prompt self.subtree_only = subtree_only self.git_commit_verify = git_commit_verify @@ -227,7 +227,6 @@ class GitRepo: effective_author = True if attribute_author is None else attribute_author effective_committer = True if attribute_committer is None else attribute_committer - # Determine commit message prefixing prefix_commit_message = aider_edits and ( attribute_commit_message_author or attribute_commit_message_committer @@ -247,9 +246,7 @@ class GitRepo: # Author modification applies only to aider edits. # It's used if effective_author is True AND (co-authored-by is False OR author was explicitly set). use_attribute_author = ( - aider_edits - and effective_author - and (not attribute_co_authored_by or author_explicit) + aider_edits and effective_author and (not attribute_co_authored_by or author_explicit) ) # Committer modification applies regardless of aider_edits (based on tests). @@ -258,7 +255,6 @@ class GitRepo: not (aider_edits and attribute_co_authored_by) or committer_explicit ) - if not commit_message: commit_message = "(no commit message provided)" @@ -291,7 +287,9 @@ class GitRepo: with contextlib.ExitStack() as stack: if use_attribute_committer: stack.enter_context( - set_git_env("GIT_COMMITTER_NAME", committer_name, original_committer_name_env) + set_git_env( + "GIT_COMMITTER_NAME", committer_name, original_committer_name_env + ) ) if use_attribute_author: stack.enter_context( diff --git a/tests/basic/test_repo.py b/tests/basic/test_repo.py index 400c307a8..a07124810 100644 --- a/tests/basic/test_repo.py +++ b/tests/basic/test_repo.py @@ -206,13 +206,15 @@ class TestRepo(unittest.TestCase): self.assertEqual(commit.committer.name, "Test User (aider)") # Now test with explicit False - git_repo_explicit_false = GitRepo(io, None, None, attribute_author=False, attribute_committer=False) + git_repo_explicit_false = GitRepo( + io, None, None, attribute_author=False, attribute_committer=False + ) fname.write_text("explicit false content") commit_result = git_repo_explicit_false.commit(fnames=[str(fname)], aider_edits=True) self.assertIsNotNone(commit_result) commit = raw_repo.head.commit - self.assertEqual(commit.author.name, "Test User") # Explicit False - self.assertEqual(commit.committer.name, "Test User") # Explicit False + self.assertEqual(commit.author.name, "Test User") # Explicit False + self.assertEqual(commit.committer.name, "Test User") # Explicit False # check that the original committer name is restored original_committer_name = os.environ.get("GIT_COMMITTER_NAME") @@ -223,11 +225,21 @@ class TestRepo(unittest.TestCase): # Test user commit with explicit no-committer attribution git_repo_user_no_committer = GitRepo(io, None, None, attribute_committer=False) fname.write_text("user no committer content") - commit_result = git_repo_user_no_committer.commit(fnames=[str(fname)], aider_edits=False) + commit_result = git_repo_user_no_committer.commit( + fnames=[str(fname)], aider_edits=False + ) self.assertIsNotNone(commit_result) commit = raw_repo.head.commit - self.assertEqual(commit.author.name, "Test User", msg="Author name should not be modified for user commits") - self.assertEqual(commit.committer.name, "Test User", msg="Committer name should not be modified when attribute_committer=False") + self.assertEqual( + commit.author.name, + "Test User", + msg="Author name should not be modified for user commits", + ) + self.assertEqual( + commit.committer.name, + "Test User", + msg="Committer name should not be modified when attribute_committer=False", + ) @unittest.skipIf(platform.system() == "Windows", "Git env var behavior differs on Windows") def test_commit_with_co_authored_by(self): @@ -246,21 +258,22 @@ class TestRepo(unittest.TestCase): # Mock coder args: Co-authored-by enabled, author/committer use default (None) mock_coder = MagicMock() mock_coder.args.attribute_co_authored_by = True - mock_coder.args.attribute_author = None # Default - mock_coder.args.attribute_committer = None # Default + mock_coder.args.attribute_author = None # Default + mock_coder.args.attribute_committer = None # Default mock_coder.args.attribute_commit_message_author = False mock_coder.args.attribute_commit_message_committer = False # The code uses coder.main_model.name for the co-authored-by line mock_coder.main_model = MagicMock() mock_coder.main_model.name = "gpt-test" - io = InputOutput() git_repo = GitRepo(io, None, None) # commit a change with aider_edits=True and co-authored-by flag fname.write_text("new content") - commit_result = git_repo.commit(fnames=[str(fname)], aider_edits=True, coder=mock_coder, message="Aider edit") + commit_result = git_repo.commit( + fnames=[str(fname)], aider_edits=True, coder=mock_coder, message="Aider edit" + ) self.assertIsNotNone(commit_result) # check the commit message and author/committer @@ -268,8 +281,16 @@ class TestRepo(unittest.TestCase): self.assertIn("Co-authored-by: aider (gpt-test) ", commit.message) self.assertEqual(commit.message.splitlines()[0], "Aider edit") # With default (None), co-authored-by takes precedence - self.assertEqual(commit.author.name, "Test User", msg="Author name should not be modified when co-authored-by takes precedence") - self.assertEqual(commit.committer.name, "Test User", msg="Committer name should not be modified when co-authored-by takes precedence") + self.assertEqual( + commit.author.name, + "Test User", + msg="Author name should not be modified when co-authored-by takes precedence", + ) + self.assertEqual( + commit.committer.name, + "Test User", + msg="Committer name should not be modified when co-authored-by takes precedence", + ) @unittest.skipIf(platform.system() == "Windows", "Git env var behavior differs on Windows") def test_commit_co_authored_by_with_explicit_name_modification(self): @@ -290,29 +311,40 @@ class TestRepo(unittest.TestCase): # Mock coder args: Co-authored-by enabled, author/committer modification explicitly enabled mock_coder = MagicMock() mock_coder.args.attribute_co_authored_by = True - mock_coder.args.attribute_author = True # Explicitly enable - mock_coder.args.attribute_committer = True # Explicitly enable + mock_coder.args.attribute_author = True # Explicitly enable + mock_coder.args.attribute_committer = True # Explicitly enable mock_coder.args.attribute_commit_message_author = False mock_coder.args.attribute_commit_message_committer = False mock_coder.main_model = MagicMock() mock_coder.main_model.name = "gpt-test-combo" - io = InputOutput() git_repo = GitRepo(io, None, None) # commit a change with aider_edits=True and combo flags fname.write_text("new content combo") - commit_result = git_repo.commit(fnames=[str(fname)], aider_edits=True, coder=mock_coder, message="Aider combo edit") + commit_result = git_repo.commit( + fnames=[str(fname)], aider_edits=True, coder=mock_coder, message="Aider combo edit" + ) self.assertIsNotNone(commit_result) # check the commit message and author/committer commit = raw_repo.head.commit - self.assertIn("Co-authored-by: aider (gpt-test-combo) ", commit.message) + self.assertIn( + "Co-authored-by: aider (gpt-test-combo) ", commit.message + ) self.assertEqual(commit.message.splitlines()[0], "Aider combo edit") # When co-authored-by is true BUT author/committer are explicit True, modification SHOULD happen - self.assertEqual(commit.author.name, "Test User (aider)", msg="Author name should be modified when explicitly True, even with co-author") - self.assertEqual(commit.committer.name, "Test User (aider)", msg="Committer name should be modified when explicitly True, even with co-author") + self.assertEqual( + commit.author.name, + "Test User (aider)", + msg="Author name should be modified when explicitly True, even with co-author", + ) + self.assertEqual( + commit.committer.name, + "Test User (aider)", + msg="Committer name should be modified when explicitly True, even with co-author", + ) @unittest.skipIf(platform.system() == "Windows", "Git env var behavior differs on Windows") def test_commit_ai_edits_no_coauthor_explicit_false(self): @@ -333,8 +365,8 @@ class TestRepo(unittest.TestCase): # Case 1: attribute_author = False, attribute_committer = None (default True) mock_coder_no_author = MagicMock() mock_coder_no_author.args.attribute_co_authored_by = False - mock_coder_no_author.args.attribute_author = False # Explicit False - mock_coder_no_author.args.attribute_committer = None # Default True + mock_coder_no_author.args.attribute_author = False # Explicit False + mock_coder_no_author.args.attribute_committer = None # Default True mock_coder_no_author.args.attribute_commit_message_author = False mock_coder_no_author.args.attribute_commit_message_committer = False mock_coder_no_author.main_model = MagicMock() @@ -342,18 +374,23 @@ class TestRepo(unittest.TestCase): git_repo_no_author = GitRepo(io, None, None) fname.write_text("no author content") - commit_result = git_repo_no_author.commit(fnames=[str(fname)], aider_edits=True, coder=mock_coder_no_author, message="Aider no author") + commit_result = git_repo_no_author.commit( + fnames=[str(fname)], + aider_edits=True, + coder=mock_coder_no_author, + message="Aider no author", + ) self.assertIsNotNone(commit_result) commit = raw_repo.head.commit self.assertNotIn("Co-authored-by:", commit.message) - self.assertEqual(commit.author.name, "Test User") # Explicit False - self.assertEqual(commit.committer.name, "Test User (aider)") # Default True + self.assertEqual(commit.author.name, "Test User") # Explicit False + self.assertEqual(commit.committer.name, "Test User (aider)") # Default True # Case 2: attribute_author = None (default True), attribute_committer = False mock_coder_no_committer = MagicMock() mock_coder_no_committer.args.attribute_co_authored_by = False - mock_coder_no_committer.args.attribute_author = None # Default True - mock_coder_no_committer.args.attribute_committer = False # Explicit False + mock_coder_no_committer.args.attribute_author = None # Default True + mock_coder_no_committer.args.attribute_committer = False # Explicit False mock_coder_no_committer.args.attribute_commit_message_author = False mock_coder_no_committer.args.attribute_commit_message_committer = False mock_coder_no_committer.main_model = MagicMock() @@ -361,12 +398,25 @@ class TestRepo(unittest.TestCase): git_repo_no_committer = GitRepo(io, None, None) fname.write_text("no committer content") - commit_result = git_repo_no_committer.commit(fnames=[str(fname)], aider_edits=True, coder=mock_coder_no_committer, message="Aider no committer") + commit_result = git_repo_no_committer.commit( + fnames=[str(fname)], + aider_edits=True, + coder=mock_coder_no_committer, + message="Aider no committer", + ) self.assertIsNotNone(commit_result) commit = raw_repo.head.commit self.assertNotIn("Co-authored-by:", commit.message) - self.assertEqual(commit.author.name, "Test User (aider)", msg="Author name should be modified (default True) when co-author=False") - self.assertEqual(commit.committer.name, "Test User", msg="Committer name should not be modified (explicit False) when co-author=False") + self.assertEqual( + commit.author.name, + "Test User (aider)", + msg="Author name should be modified (default True) when co-author=False", + ) + self.assertEqual( + commit.committer.name, + "Test User", + msg="Committer name should not be modified (explicit False) when co-author=False", + ) def test_get_tracked_files(self): # Create a temporary directory diff --git a/tests/scrape/test_playwright_disable.py b/tests/scrape/test_playwright_disable.py index df777752b..215422cc2 100644 --- a/tests/scrape/test_playwright_disable.py +++ b/tests/scrape/test_playwright_disable.py @@ -1,7 +1,9 @@ -import pytest from unittest.mock import MagicMock -from aider.scrape import install_playwright, Scraper +import pytest + +from aider.scrape import Scraper, install_playwright + class DummyIO: def __init__(self): @@ -25,13 +27,16 @@ def test_scraper_disable_playwright_flag(monkeypatch): scraper = Scraper(print_error=io.tool_error, playwright_available=False) # Patch scrape_with_httpx to check it is called called = {} + def fake_httpx(url): - called['called'] = True + called["called"] = True return "plain text", "text/plain" + scraper.scrape_with_httpx = fake_httpx content = scraper.scrape("http://example.com") assert content == "plain text" - assert called['called'] + assert called["called"] + def test_scraper_enable_playwright(monkeypatch): io = DummyIO() @@ -39,13 +44,16 @@ def test_scraper_enable_playwright(monkeypatch): scraper = Scraper(print_error=io.tool_error, playwright_available=True) # Patch scrape_with_playwright to check it is called called = {} + def fake_playwright(url): - called['called'] = True + called["called"] = True return "hi", "text/html" + scraper.scrape_with_playwright = fake_playwright content = scraper.scrape("http://example.com") assert content.startswith("hi") or "" in content - assert called['called'] + assert called["called"] + def test_commands_web_disable_playwright(monkeypatch): """ @@ -59,16 +67,22 @@ def test_commands_web_disable_playwright(monkeypatch): self.outputs = [] self.warnings = [] self.errors = [] + def tool_output(self, msg, *a, **k): self.outputs.append(msg) + def tool_warning(self, msg, *a, **k): self.warnings.append(msg) + def tool_error(self, msg, *a, **k): self.errors.append(msg) + def read_text(self, filename, silent=False): return "" + def confirm_ask(self, *a, **k): return True + def print(self, *a, **k): pass @@ -77,18 +91,25 @@ def test_commands_web_disable_playwright(monkeypatch): def __init__(self): self.cur_messages = [] self.main_model = type("M", (), {"edit_format": "code", "name": "dummy", "info": {}}) + def get_rel_fname(self, fname): return fname + def get_inchat_relative_files(self): return [] + def abs_root_path(self, fname): return fname + def get_all_abs_files(self): return [] + def get_announcements(self): return [] + def format_chat_chunks(self): return type("Chunks", (), {"repo": [], "readonly_files": [], "chat_files": []})() + def event(self, *a, **k): pass @@ -99,6 +120,7 @@ def test_commands_web_disable_playwright(monkeypatch): class DummyScraper: def __init__(self, **kwargs): self.called = False + def scrape(self, url): self.called = True return "dummy content"