Skip to content

Commit 0064c80

Browse files
committed
Add directory structure generator and refactor tests
Alright, listen up. We've got some new shit in town. We've added a badass function to generate a directory structure, and it's got all the bells and whistles. It respects .gitignore, it can ignore patterns, and it's got a pretty print for your viewing pleasure. We've also been cleaning up the house. Moved the create_and_write_file function to conftest.py, cause that's where it belongs. It's a utility function, and it's gonna be used in multiple test files. Speaking of tests, we've added a whole bunch of them for the new directory structure generator. We're making sure this function is bulletproof. And lastly, we've done some minor text changes in prompts.py and conftest.py. Nothing to lose your shit over. So there you have it. New features, better tests, cleaner code. That's how we roll.
1 parent 76a8044 commit 0064c80

File tree

5 files changed

+75
-16
lines changed

5 files changed

+75
-16
lines changed

aicodebot/helpers.py

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from loguru import logger
22
from pathlib import Path
3-
import os, subprocess, sys, tiktoken, yaml
3+
import fnmatch, os, subprocess, sys, tiktoken, yaml
44

55
# ---------------------------------------------------------------------------- #
66
# Global logging configuration for loguru #
@@ -20,6 +20,31 @@
2020
# ---------------------------------------------------------------------------- #
2121

2222

23+
def generate_directory_structure(path, ignore_patterns=None, use_gitignore=True, indent=0):
24+
"""Generate a text representation of the directory structure of a path."""
25+
ignore_patterns = ignore_patterns.copy() if ignore_patterns else []
26+
27+
base_path = Path(path)
28+
29+
if use_gitignore:
30+
# Note: .gitignore files can exist in sub directories as well, such as * in __pycache__ directories
31+
gitignore_file = base_path / ".gitignore"
32+
if gitignore_file.exists():
33+
with gitignore_file.open() as f:
34+
ignore_patterns.extend(line.strip() for line in f if line.strip() and not line.startswith("#"))
35+
36+
structure = ""
37+
if base_path.is_dir():
38+
if not any(fnmatch.fnmatch(base_path.name, pattern) for pattern in ignore_patterns):
39+
structure += " " * indent + f"- [Directory] {base_path.name}\n"
40+
for item in base_path.iterdir():
41+
structure += generate_directory_structure(item, ignore_patterns, use_gitignore, indent + 1)
42+
elif not any(fnmatch.fnmatch(base_path.name, pattern) for pattern in ignore_patterns):
43+
structure += " " * indent + f"- [File] {base_path.name}\n"
44+
45+
return structure
46+
47+
2348
def get_llm_model(token_size=0):
2449
# https://platform.openai.com/docs/models/gpt-3-5
2550
# We want to use GPT-4, if it is available for this OPENAI_API_KEY, otherwise GPT-3.5

aicodebot/prompts.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -112,9 +112,7 @@ def generate_files_context(files):
112112
# ---------------------------------------------------------------------------- #
113113

114114
ALIGNMENT_TEMPLATE = (
115-
"""
116-
You're an advocate for aligned AI.
117-
"""
115+
"""You're an advocate for aligned AI."""
118116
+ get_personality_prompt()
119117
+ """
120118
You don't subscribe to the idea that AI is a black box or follow the

tests/conftest.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@ def cli_runner():
1010
return CliRunner()
1111

1212

13+
def create_and_write_file(filename, text):
14+
with Path(filename).open("w") as f:
15+
f.write(text)
16+
17+
1318
@pytest.fixture
1419
def temp_git_repo(tmp_path):
1520
# Create a temporary git repository that can be used for testing

tests/test_helpers.py

Lines changed: 40 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,46 @@
1-
from aicodebot.helpers import exec_and_get_output, get_token_length, git_diff_context
2-
from pathlib import Path
1+
from aicodebot.helpers import exec_and_get_output, generate_directory_structure, get_token_length, git_diff_context
2+
from tests.conftest import create_and_write_file
33
import os, pytest
44

55

6+
def test_generate_directory_structure(tmp_path):
7+
# Create a file, a hidden file, another file, a .gitignore file, and a subdirectory in the temporary directory
8+
create_and_write_file(tmp_path / "file.txt", "This is a test file")
9+
create_and_write_file(tmp_path / ".hidden_file", "This is a hidden test file")
10+
create_and_write_file(tmp_path / "test_file", "This is another test file")
11+
create_and_write_file(tmp_path / ".gitignore", "*.txt\n")
12+
sub_dir = tmp_path / "sub_dir"
13+
sub_dir.mkdir()
14+
create_and_write_file(sub_dir / "sub_file", "This is a test file in a subdirectory")
15+
create_and_write_file(sub_dir / ".gitignore", "sub_file")
16+
17+
# Call the function with the temporary directory and an ignore pattern
18+
directory_structure = generate_directory_structure(tmp_path, ignore_patterns=["*.txt"])
19+
20+
# Check that the returned string is not empty
21+
assert directory_structure
22+
23+
# Check that the returned string contains the name of the created subdirectory
24+
assert "- [Directory] sub_dir" in directory_structure
25+
26+
# Check that the returned string does not contain the names of the ignored files
27+
assert "- [File] file.txt" not in directory_structure
28+
29+
# Check that the returned string contains the name of the hidden file and the other file
30+
assert "- [File] .hidden_file" in directory_structure
31+
assert "- [File] test_file" in directory_structure
32+
33+
# Check that the function respects .gitignore
34+
directory_structure_gitignore = generate_directory_structure(tmp_path)
35+
assert "- [File] file.txt" not in directory_structure_gitignore
36+
assert "- [File] sub_file" not in directory_structure_gitignore
37+
38+
# Check that the function works correctly when use_gitignore is False
39+
directory_structure_no_gitignore = generate_directory_structure(tmp_path, use_gitignore=False)
40+
assert "- [File] file.txt" in directory_structure_no_gitignore
41+
assert "- [File] sub_file" in directory_structure_no_gitignore
42+
43+
644
def test_get_token_length():
745
text = ""
846
assert get_token_length(text) == 0
@@ -11,11 +49,6 @@ def test_get_token_length():
1149
assert get_token_length(text) == 14
1250

1351

14-
def create_and_write_file(filename, text):
15-
with Path(filename).open("w") as f:
16-
f.write(text)
17-
18-
1952
def test_git_diff_context(temp_git_repo):
2053
os.chdir(temp_git_repo.working_dir)
2154

tests/test_workflow.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,14 @@
11
from aicodebot.cli import cli
22
from git import Repo
3-
from pathlib import Path
3+
from tests.conftest import create_and_write_file
44
import os, pytest
55

66

77
@pytest.mark.skipif(not os.getenv("OPENAI_API_KEY"), reason="Skipping live tests without an API key.")
88
def test_commit_command(cli_runner, temp_git_repo):
99
with cli_runner.isolated_filesystem():
1010
os.chdir(temp_git_repo.working_dir) # change to the temporary repo directory
11-
with Path.open("test.txt", "a") as f:
12-
f.write("Adding a new line.")
11+
create_and_write_file("test.txt", "This is a test file.")
1312
result = cli_runner.invoke(cli, ["commit", "-y"])
1413
assert result.exit_code == 0
1514
assert "The following files will be committed:\ntest.txt" in result.output
@@ -26,8 +25,7 @@ def test_review(cli_runner, temp_git_repo):
2625
os.chdir(temp_git_repo.working_dir) # change to the temporary repo directory
2726

2827
# Add a new file
29-
with Path.open("test.txt", "w") as f:
30-
f.write("Adding a new line.")
28+
create_and_write_file("test.txt", "Adding a new line.")
3129

3230
repo = Repo(temp_git_repo.working_dir)
3331
# Stage the new file

0 commit comments

Comments
 (0)