mirror of
https://github.com/Aider-AI/aider.git
synced 2025-05-30 17:24:59 +00:00
Merge remote-tracking branch 'origin/main'
This commit is contained in:
commit
5748a575f8
5 changed files with 128 additions and 14 deletions
|
@ -1,5 +1,5 @@
|
|||
repos:
|
||||
- repo: https://github.com/pycqa/isort
|
||||
- repo: https://github.com/PyCQA/isort
|
||||
rev: 5.12.0
|
||||
hooks:
|
||||
- id: isort
|
||||
|
|
|
@ -12,6 +12,21 @@ from aider.args_formatter import MarkdownHelpFormatter, YamlHelpFormatter
|
|||
from .dump import dump # noqa: F401
|
||||
|
||||
|
||||
def default_env_file(git_root):
|
||||
return os.path.join(git_root, ".env") if git_root else ".env"
|
||||
|
||||
|
||||
def get_preparser(git_root):
|
||||
parser = configargparse.ArgumentParser(add_help=False)
|
||||
parser.add_argument(
|
||||
"--env-file",
|
||||
metavar="ENV_FILE",
|
||||
default=default_env_file(git_root),
|
||||
help="Specify the .env file to load (default: .env in git root)",
|
||||
)
|
||||
return parser
|
||||
|
||||
|
||||
def get_parser(default_config_files, git_root):
|
||||
parser = configargparse.ArgumentParser(
|
||||
description="aider is GPT powered coding in your terminal",
|
||||
|
@ -184,11 +199,12 @@ def get_parser(default_config_files, git_root):
|
|||
" max_chat_history_tokens."
|
||||
),
|
||||
)
|
||||
default_env_file = os.path.join(git_root, ".env") if git_root else ".env"
|
||||
# This is a duplicate of the argument in the preparser and is a no-op by this time of
|
||||
# argument parsing, but it's here so that the help is displayed as expected.
|
||||
group.add_argument(
|
||||
"--env-file",
|
||||
metavar="ENV_FILE",
|
||||
default=default_env_file,
|
||||
default=default_env_file(git_root),
|
||||
help="Specify the .env file to load (default: .env in git root)",
|
||||
)
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ from prompt_toolkit.enums import EditingMode
|
|||
from streamlit.web import cli
|
||||
|
||||
from aider import __version__, models, utils
|
||||
from aider.args import get_parser
|
||||
from aider.args import get_parser, get_preparser
|
||||
from aider.coders import Coder
|
||||
from aider.commands import SwitchModel
|
||||
from aider.io import InputOutput
|
||||
|
@ -124,12 +124,18 @@ def check_gitignore(git_root, io, ask=True):
|
|||
|
||||
def format_settings(parser, args):
|
||||
show = scrub_sensitive_info(args, parser.format_values())
|
||||
# clean up the headings for consistency w/ new lines
|
||||
heading_env = "Environment Variables:"
|
||||
heading_defaults = "Defaults:"
|
||||
if heading_env in show:
|
||||
show = show.replace(heading_env, "\n" + heading_env)
|
||||
show = show.replace(heading_defaults, "\n" + heading_defaults)
|
||||
show += "\n"
|
||||
show += "Option settings:\n"
|
||||
for arg, val in sorted(vars(args).items()):
|
||||
if val:
|
||||
val = scrub_sensitive_info(args, str(val))
|
||||
show += f" - {arg}: {val}\n"
|
||||
show += f" - {arg}: {val}\n" # noqa: E221
|
||||
return show
|
||||
|
||||
|
||||
|
@ -225,6 +231,12 @@ def main(argv=None, input=None, output=None, force_git_root=None, return_coder=F
|
|||
default_config_files.append(Path.home() / conf_fname) # homedir
|
||||
default_config_files = list(map(str, default_config_files))
|
||||
|
||||
preparser = get_preparser(git_root)
|
||||
pre_args, _ = preparser.parse_known_args(argv)
|
||||
|
||||
# Load the .env file specified in the arguments
|
||||
load_dotenv(pre_args.env_file)
|
||||
|
||||
parser = get_parser(default_config_files, git_root)
|
||||
args = parser.parse_args(argv)
|
||||
|
||||
|
@ -320,9 +332,6 @@ def main(argv=None, input=None, output=None, force_git_root=None, return_coder=F
|
|||
cmd_line = scrub_sensitive_info(args, cmd_line)
|
||||
io.tool_output(cmd_line, log_only=True)
|
||||
|
||||
if args.env_file:
|
||||
load_dotenv(args.env_file)
|
||||
|
||||
if args.anthropic_api_key:
|
||||
os.environ["ANTHROPIC_API_KEY"] = args.anthropic_api_key
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
import tempfile
|
||||
from io import StringIO
|
||||
from pathlib import Path
|
||||
from unittest import TestCase
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
@ -13,24 +13,28 @@ from prompt_toolkit.output import DummyOutput
|
|||
from aider.dump import dump # noqa: F401
|
||||
from aider.io import InputOutput
|
||||
from aider.main import check_gitignore, main, setup_git
|
||||
from aider.utils import GitTemporaryDirectory, make_repo
|
||||
from aider.utils import GitTemporaryDirectory, IgnorantTemporaryDirectory, make_repo
|
||||
|
||||
|
||||
class TestMain(TestCase):
|
||||
def setUp(self):
|
||||
self.original_env = os.environ.copy()
|
||||
os.environ["OPENAI_API_KEY"] = "deadbeef"
|
||||
self.original_cwd = os.getcwd()
|
||||
self.tempdir = tempfile.mkdtemp()
|
||||
self.tempdir_obj = IgnorantTemporaryDirectory()
|
||||
self.tempdir = self.tempdir_obj.name
|
||||
os.chdir(self.tempdir)
|
||||
|
||||
def tearDown(self):
|
||||
os.chdir(self.original_cwd)
|
||||
shutil.rmtree(self.tempdir, ignore_errors=True)
|
||||
self.tempdir_obj.cleanup()
|
||||
os.environ.clear()
|
||||
os.environ.update(self.original_env)
|
||||
|
||||
def test_main_with_empty_dir_no_files_on_command(self):
|
||||
main(["--no-git"], input=DummyInput(), output=DummyOutput())
|
||||
|
||||
def test_main_with_empty_dir_new_file(self):
|
||||
def test_main_with_emptqy_dir_new_file(self):
|
||||
main(["foo.txt", "--yes", "--no-git"], input=DummyInput(), output=DummyOutput())
|
||||
self.assertTrue(os.path.exists("foo.txt"))
|
||||
|
||||
|
@ -237,3 +241,82 @@ class TestMain(TestCase):
|
|||
main(["--message", test_message])
|
||||
args, kwargs = MockInputOutput.call_args
|
||||
self.assertEqual(args[1], None)
|
||||
|
||||
def test_dark_mode_sets_code_theme(self):
|
||||
# Mock Coder.create to capture the configuration
|
||||
with patch("aider.coders.Coder.create") as MockCoder:
|
||||
main(["--dark-mode", "--no-git"], input=DummyInput(), output=DummyOutput())
|
||||
# Ensure Coder.create was called
|
||||
MockCoder.assert_called_once()
|
||||
# Check if the code_theme setting is for dark mode
|
||||
_, kwargs = MockCoder.call_args
|
||||
self.assertEqual(kwargs["code_theme"], "monokai")
|
||||
|
||||
def test_light_mode_sets_code_theme(self):
|
||||
# Mock Coder.create to capture the configuration
|
||||
with patch("aider.coders.Coder.create") as MockCoder:
|
||||
main(["--light-mode", "--no-git"], input=DummyInput(), output=DummyOutput())
|
||||
# Ensure Coder.create was called
|
||||
MockCoder.assert_called_once()
|
||||
# Check if the code_theme setting is for light mode
|
||||
_, kwargs = MockCoder.call_args
|
||||
self.assertEqual(kwargs["code_theme"], "default")
|
||||
|
||||
def create_env_file(self, file_name, content):
|
||||
env_file_path = Path(self.tempdir) / file_name
|
||||
env_file_path.write_text(content)
|
||||
return env_file_path
|
||||
|
||||
def test_env_file_flag_sets_automatic_variable(self):
|
||||
env_file_path = self.create_env_file(".env.test", "AIDER_DARK_MODE=True")
|
||||
with patch("aider.coders.Coder.create") as MockCoder:
|
||||
main(
|
||||
["--env-file", str(env_file_path), "--no-git"],
|
||||
input=DummyInput(),
|
||||
output=DummyOutput(),
|
||||
)
|
||||
MockCoder.assert_called_once()
|
||||
# Check if the color settings are for dark mode
|
||||
_, kwargs = MockCoder.call_args
|
||||
self.assertEqual(kwargs["code_theme"], "monokai")
|
||||
|
||||
def test_default_env_file_sets_automatic_variable(self):
|
||||
self.create_env_file(".env", "AIDER_DARK_MODE=True")
|
||||
with patch("aider.coders.Coder.create") as MockCoder:
|
||||
main(["--no-git"], input=DummyInput(), output=DummyOutput())
|
||||
# Ensure Coder.create was called
|
||||
MockCoder.assert_called_once()
|
||||
# Check if the color settings are for dark mode
|
||||
_, kwargs = MockCoder.call_args
|
||||
self.assertEqual(kwargs["code_theme"], "monokai")
|
||||
|
||||
def test_false_vals_in_env_file(self):
|
||||
self.create_env_file(".env", "AIDER_SHOW_DIFFS=off")
|
||||
with patch("aider.coders.Coder.create") as MockCoder:
|
||||
main(["--no-git"], input=DummyInput(), output=DummyOutput())
|
||||
MockCoder.assert_called_once()
|
||||
_, kwargs = MockCoder.call_args
|
||||
self.assertEqual(kwargs["show_diffs"], False)
|
||||
|
||||
def test_true_vals_in_env_file(self):
|
||||
self.create_env_file(".env", "AIDER_SHOW_DIFFS=on")
|
||||
with patch("aider.coders.Coder.create") as MockCoder:
|
||||
main(["--no-git"], input=DummyInput(), output=DummyOutput())
|
||||
MockCoder.assert_called_once()
|
||||
_, kwargs = MockCoder.call_args
|
||||
self.assertEqual(kwargs["show_diffs"], True)
|
||||
|
||||
def test_verbose_mode_lists_env_vars(self):
|
||||
self.create_env_file(".env", "AIDER_DARK_MODE=on")
|
||||
with patch("sys.stdout", new_callable=StringIO) as mock_stdout:
|
||||
main(["--no-git", "--verbose"], input=DummyInput(), output=DummyOutput())
|
||||
output = mock_stdout.getvalue()
|
||||
relevant_output = "\n".join(
|
||||
line
|
||||
for line in output.splitlines()
|
||||
if "AIDER_DARK_MODE" in line or "dark_mode" in line
|
||||
) # this bit just helps failing assertions to be easier to read
|
||||
self.assertIn("AIDER_DARK_MODE", relevant_output)
|
||||
self.assertIn("dark_mode", relevant_output)
|
||||
self.assertRegex(relevant_output, r"AIDER_DARK_MODE:\s+on")
|
||||
self.assertRegex(relevant_output, r"dark_mode:\s+True")
|
||||
|
|
|
@ -17,11 +17,17 @@ class IgnorantTemporaryDirectory:
|
|||
return self.temp_dir.__enter__()
|
||||
|
||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||
self.cleanup()
|
||||
|
||||
def cleanup(self):
|
||||
try:
|
||||
self.temp_dir.__exit__(exc_type, exc_val, exc_tb)
|
||||
self.temp_dir.cleanup()
|
||||
except (OSError, PermissionError):
|
||||
pass # Ignore errors (Windows)
|
||||
|
||||
def __getattr__(self, item):
|
||||
return getattr(self.temp_dir, item)
|
||||
|
||||
|
||||
class ChdirTemporaryDirectory(IgnorantTemporaryDirectory):
|
||||
def __init__(self):
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue