diff --git a/pyproject.toml b/pyproject.toml index a250af75..8d1d1b13 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api" [tool.poetry] name = "readmeai" -version = "0.4.056" +version = "0.4.057" description = "🚀 Generate beautiful README files from the terminal, powered by OpenAI's GPT language models 💫" authors = ["Eli <0x.eli.64s@gmail.com>"] license = "MIT" diff --git a/readmeai/core/model.py b/readmeai/core/model.py index e48f6e5a..ee8f4f64 100644 --- a/readmeai/core/model.py +++ b/readmeai/core/model.py @@ -201,7 +201,7 @@ async def generate_text( except openai.error.OpenAIError as excinfo: self.logger.error(f"OpenAI Exception:\n{str(excinfo)}") return await self.null_summary( - index, f"OpenAI exception: {excinfo.response.status_code}" + index, f"OpenAI Exception: {excinfo.response.status_code}" ) except httpx.HTTPStatusError as excinfo: diff --git a/readmeai/utils/utils.py b/readmeai/utils/utils.py index 38208c6c..662239a5 100644 --- a/readmeai/utils/utils.py +++ b/readmeai/utils/utils.py @@ -1,4 +1,4 @@ -"""Utility methods for the readme-ai application.""" +"""Utility functions for the readmeai package.""" import re from pathlib import Path @@ -35,8 +35,18 @@ def is_valid_url(url: str) -> bool: return re.match(regex, url) is not None -def flatten_list(nested_list: List) -> List: - """Flattens a nested list.""" +def flatten_list(nested_list: List[List]) -> List: + """Flatten a nested list (list of lists converted to a single list). + + Parameters + ---------- + nested_list + The nested list to flatten. + + Returns + ------- + A flattened list. + """ result = [] for item in nested_list: if isinstance(item, list): @@ -47,7 +57,21 @@ def flatten_list(nested_list: List) -> List: def format_sentence(text: str) -> str: - """Clean and format the generated text from the model.""" + """Formats output text from the LLM model. + i.e. removes extra white space, newlines, tabs, etc. + + Parameters + ---------- + text + The text to format. + + Returns + ------- + The formatted text, used to generate the README. + """ + # Remove single and double quotes around any text + text = re.sub(r"(? str: # Remove extra white space around hyphens text = re.sub(r"\s*-\s*", "-", text) - return text.strip().strip('"') + return text.strip() def get_relative_path(absolute_path: str, base_path: str) -> str: - """Get the relative path of a file.""" + """Get the relative path of a file. + + Parameters + ---------- + absolute_path + Absolute path to the file i.e. the full path on the local machine. + base_path + Base path to use for the relative path i.e. the project root. + + Returns + ------- + The relative path string of the file. + """ absolute_path = Path(absolute_path) return absolute_path.relative_to(base_path) -def remove_substring(input_string: str) -> str: - """Remove text between HTML tags.""" - pattern = r"

.*?" +def remove_substring( + input_string: str, pattern: str = r"

.*?" +) -> str: + """Remove a substring from a string. + + Parameters + ---------- + input_string + The string to remove the substring from. + pattern, optional + The substring to remove, by default r"

.*?". + + Returns + ------- + The input string with the substring removed. + """ output_string = re.sub( pattern, "

\n", input_string, flags=re.DOTALL ) @@ -85,7 +134,20 @@ def remove_substring(input_string: str) -> str: def should_ignore(conf_helper: ConfigHelper, file_path: Path) -> bool: - """Filters out files that should be ignored.""" + """Check if a file should be ignored and not passed to the LLM API. + Uses a default list of files - 'readmeai/settings/ignore_files.toml'. + + Parameters + ---------- + conf_helper + Configuration helper instance with the ignore files list. + file_path + The path to the file to check. + + Returns + ------- + True if the file should be ignored, False otherwise. + """ ignore_files = conf_helper.ignore_files if ( @@ -96,7 +158,7 @@ def should_ignore(conf_helper: ConfigHelper, file_path: Path) -> bool: for directory in ignore_files["directories"] ) ): - logger.debug(f"Ignoring: {file_path.name}") + logger.debug(f"Ignoring item: {file_path.name}") return True return False diff --git a/tests/conftest.py b/tests/conftest.py index 39d12a3c..6ca7f0de 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,18 +1,17 @@ """Pytest configuration file.""" -from pathlib import Path - import pytest -import toml + +from readmeai.config.settings import AppConfigModel, ConfigHelper, load_config @pytest.fixture(scope="session") def config(): - current_dir = Path(__file__).parent - parent_dir = current_dir.parent - config_path = parent_dir / "settings/config.toml" + config_dict = load_config() + config_model = AppConfigModel(app=config_dict) + return config_model - with open(config_path, "r") as file: - config = toml.load(file) - return config +@pytest.fixture(scope="session") +def config_helper(config): + return ConfigHelper(config) diff --git a/tests/test_config/test_settings.py b/tests/test_config/test_settings.py index 964af0f3..67193369 100644 --- a/tests/test_config/test_settings.py +++ b/tests/test_config/test_settings.py @@ -1,10 +1,10 @@ """Unit tests for readmeai.settings.""" from readmeai.config.settings import ( - GitHost, + BadgeCliOptions, GitApiUrl, GitFileUrl, - BadgeCliOptions, + GitHost, ) from readmeai.utils import utils diff --git a/tests/test_utils/test_utils.py b/tests/test_utils/test_utils.py index e69de29b..9fa1c499 100644 --- a/tests/test_utils/test_utils.py +++ b/tests/test_utils/test_utils.py @@ -0,0 +1,69 @@ +"""Unit tests for utils.py.""" + +from pathlib import Path + +from readmeai.config.settings import ConfigHelper +from readmeai.utils.utils import ( + flatten_list, + format_sentence, + get_relative_path, + is_valid_url, + remove_substring, + should_ignore, +) + + +def test_is_valid_url(): + assert is_valid_url("https://www.example.com") is True + assert is_valid_url("http://www.example.com") is True + assert is_valid_url("www.example.com") is False + assert is_valid_url("example.com") is False + + +def test_flatten_list(): + nested_list = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] + flattened_list = flatten_list(nested_list) + assert flattened_list == [1, 2, 3, 4, 5, 6, 7, 8, 9] + + +def test_format_sentence(): + text = "'hello world'" + formatted_sentence = format_sentence(text) + assert formatted_sentence == "hello world" + + +def test_get_relative_path(): + """Test that the relative path is correct.""" + absolute_path = "readmeai/utils/utils.py" + base_path = Path().parent + relative_path = get_relative_path(absolute_path, base_path) + assert str(relative_path) == "readmeai/utils/utils.py" + + +def test_remove_substring(): + """Test that the substring is removed from the input string.""" + test_string = """ +
+

Paragraph 1 content.

+ Some content that should remain. +

Paragraph 2 content with

nested tag.

+ This content is outside of the div tags. +

Another paragraph.

+
Content to be removed.
+

Final paragraph.

+
+

Paragraph inside div.

+ Content inside div that should be removed. +
+
+ """ + result = remove_substring(test_string) + assert isinstance(result, str) + + +def test_should_ignore(config_helper: ConfigHelper): + """Test that the file is ignored.""" + file_path_ignored = Path("README.md") + file_path_not_ignored = Path("readmeai/main.py") + assert should_ignore(config_helper, file_path_ignored) is True + assert should_ignore(config_helper, file_path_not_ignored) is False