From 70cb55af8a93c6bc918de95800dc8a8b0ca06bf1 Mon Sep 17 00:00:00 2001 From: labrocadabro Date: Tue, 22 Apr 2025 09:34:48 -0300 Subject: [PATCH 01/19] Implement string reversal function --- src/string_reversal.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 src/string_reversal.py diff --git a/src/string_reversal.py b/src/string_reversal.py new file mode 100644 index 00000000..caeeae82 --- /dev/null +++ b/src/string_reversal.py @@ -0,0 +1,19 @@ +def reverse_string(s: str) -> str: + """ + Reverse the given string. + + Args: + s (str): The input string to be reversed. + + Returns: + str: The reversed string. + + Raises: + TypeError: If the input is not a string. + """ + # Check if input is a string + if not isinstance(s, str): + raise TypeError("Input must be a string") + + # Return the reversed string + return s[::-1] \ No newline at end of file From f40e9459f31d6cfe6bd93677059f16cf723945ec Mon Sep 17 00:00:00 2001 From: labrocadabro Date: Tue, 22 Apr 2025 09:34:57 -0300 Subject: [PATCH 02/19] Add comprehensive tests for string reversal function --- tests/test_string_reversal.py | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 tests/test_string_reversal.py diff --git a/tests/test_string_reversal.py b/tests/test_string_reversal.py new file mode 100644 index 00000000..f7a51634 --- /dev/null +++ b/tests/test_string_reversal.py @@ -0,0 +1,34 @@ +import pytest +from src.string_reversal import reverse_string + +def test_reverse_string_basic(): + """Test basic string reversal.""" + assert reverse_string("hello") == "olleh" + assert reverse_string("python") == "nohtyp" + +def test_reverse_string_empty(): + """Test reversing an empty string.""" + assert reverse_string("") == "" + +def test_reverse_string_single_char(): + """Test reversing a single character.""" + assert reverse_string("a") == "a" + +def test_reverse_string_with_spaces(): + """Test reversing a string with spaces.""" + assert reverse_string("hello world") == "dlrow olleh" + +def test_reverse_string_with_special_chars(): + """Test reversing a string with special characters.""" + assert reverse_string("a!b@c#") == "#c@b!a" + +def test_reverse_string_invalid_input(): + """Test that a TypeError is raised for non-string inputs.""" + with pytest.raises(TypeError, match="Input must be a string"): + reverse_string(123) + + with pytest.raises(TypeError, match="Input must be a string"): + reverse_string(None) + + with pytest.raises(TypeError, match="Input must be a string"): + reverse_string(["list"]) \ No newline at end of file From 136e36471b4554a17958eae8392e30d1c1628960 Mon Sep 17 00:00:00 2001 From: labrocadabro Date: Tue, 22 Apr 2025 09:35:05 -0300 Subject: [PATCH 03/19] Add pytest dependency --- requirements.txt | 1 + 1 file changed, 1 insertion(+) create mode 100644 requirements.txt diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 00000000..55b033e9 --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +pytest \ No newline at end of file From 1db32b9bab3f8c48f56a9479e8425a3e789521c4 Mon Sep 17 00:00:00 2001 From: labrocadabro Date: Tue, 22 Apr 2025 09:35:56 -0300 Subject: [PATCH 04/19] Implement string reversal without slicing or reverse() --- src/string_reversal.py | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/string_reversal.py b/src/string_reversal.py index caeeae82..6deabbf8 100644 --- a/src/string_reversal.py +++ b/src/string_reversal.py @@ -1,6 +1,6 @@ def reverse_string(s: str) -> str: """ - Reverse the given string. + Reverse the given string manually without using slicing or reverse(). Args: s (str): The input string to be reversed. @@ -15,5 +15,17 @@ def reverse_string(s: str) -> str: if not isinstance(s, str): raise TypeError("Input must be a string") - # Return the reversed string - return s[::-1] \ No newline at end of file + # Convert string to list of characters + chars = list(s) + + # Manually reverse the list of characters + left, right = 0, len(chars) - 1 + while left < right: + # Swap characters + chars[left], chars[right] = chars[right], chars[left] + # Move towards the center + left += 1 + right -= 1 + + # Convert back to string and return + return ''.join(chars) \ No newline at end of file From 33d64901bc6a2035d16e1f52333e4cb618641c1f Mon Sep 17 00:00:00 2001 From: labrocadabro Date: Tue, 22 Apr 2025 09:36:05 -0300 Subject: [PATCH 05/19] Update tests for string reversal implementation --- tests/test_string_reversal.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_string_reversal.py b/tests/test_string_reversal.py index f7a51634..a7bafa65 100644 --- a/tests/test_string_reversal.py +++ b/tests/test_string_reversal.py @@ -31,4 +31,4 @@ def test_reverse_string_invalid_input(): reverse_string(None) with pytest.raises(TypeError, match="Input must be a string"): - reverse_string(["list"]) \ No newline at end of file + reverse_string(["list"])) \ No newline at end of file From 0e4e1d41372580b863a287d7703fdadcf09c872c Mon Sep 17 00:00:00 2001 From: labrocadabro Date: Tue, 22 Apr 2025 09:36:22 -0300 Subject: [PATCH 06/19] Fix syntax error in test file --- tests/test_string_reversal.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_string_reversal.py b/tests/test_string_reversal.py index a7bafa65..f7a51634 100644 --- a/tests/test_string_reversal.py +++ b/tests/test_string_reversal.py @@ -31,4 +31,4 @@ def test_reverse_string_invalid_input(): reverse_string(None) with pytest.raises(TypeError, match="Input must be a string"): - reverse_string(["list"])) \ No newline at end of file + reverse_string(["list"]) \ No newline at end of file From 519572d3e13b2fe300d9dcb2f77a1799a2fe39b0 Mon Sep 17 00:00:00 2001 From: laura-abro Date: Tue, 22 Apr 2025 09:37:31 -0300 Subject: [PATCH 07/19] Implement array flattening function with recursive approach --- src/array_flattener.py | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 src/array_flattener.py diff --git a/src/array_flattener.py b/src/array_flattener.py new file mode 100644 index 00000000..c7196377 --- /dev/null +++ b/src/array_flattener.py @@ -0,0 +1,39 @@ +from typing import List, Union + +def flatten_array(arr: List[Union[int, List]]) -> List[int]: + """ + Recursively flatten a nested list of integers into a single-level list. + + Args: + arr (List[Union[int, List]]): A potentially nested list of integers. + + Returns: + List[int]: A flattened list containing all integers from the input. + + Raises: + TypeError: If the input is not a list or contains non-integer/non-list elements. + + Examples: + >>> flatten_array([1, [2, 3], 4]) + [1, 2, 3, 4] + >>> flatten_array([1, [2, [3, 4]], 5]) + [1, 2, 3, 4, 5] + """ + # Validate input is a list + if not isinstance(arr, list): + raise TypeError("Input must be a list") + + flattened = [] + + for item in arr: + # If item is a list, recursively flatten + if isinstance(item, list): + flattened.extend(flatten_array(item)) + # If item is an integer, append to result + elif isinstance(item, int): + flattened.append(item) + # Raise error for invalid item types + else: + raise TypeError(f"List can only contain integers or nested lists, found {type(item)}") + + return flattened \ No newline at end of file From 99fa1750a9d7dd28a9c7cf31a7404a1e7abb2d60 Mon Sep 17 00:00:00 2001 From: laura-abro Date: Tue, 22 Apr 2025 09:37:45 -0300 Subject: [PATCH 08/19] Add comprehensive tests for array flattening function --- tests/test_array_flattener.py | 46 +++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 tests/test_array_flattener.py diff --git a/tests/test_array_flattener.py b/tests/test_array_flattener.py new file mode 100644 index 00000000..1c12928b --- /dev/null +++ b/tests/test_array_flattener.py @@ -0,0 +1,46 @@ +import pytest +from src.array_flattener import flatten_array + +def test_flatten_simple_list(): + """Test flattening a simple list with no nested lists""" + assert flatten_array([1, 2, 3]) == [1, 2, 3] + +def test_flatten_single_nested_list(): + """Test flattening a list with one level of nesting""" + assert flatten_array([1, [2, 3], 4]) == [1, 2, 3, 4] + +def test_flatten_multiple_nested_lists(): + """Test flattening a list with multiple levels of nesting""" + assert flatten_array([1, [2, [3, 4]], 5]) == [1, 2, 3, 4, 5] + +def test_flatten_deeply_nested_list(): + """Test flattening a deeply nested list""" + assert flatten_array([1, [2, [3, [4, [5]]]], 6]) == [1, 2, 3, 4, 5, 6] + +def test_flatten_empty_list(): + """Test flattening an empty list""" + assert flatten_array([]) == [] + +def test_flatten_nested_empty_lists(): + """Test flattening a list with nested empty lists""" + assert flatten_array([1, [], [2, []], 3]) == [1, 2, 3] + +def test_invalid_input_non_list(): + """Test that a TypeError is raised for non-list input""" + with pytest.raises(TypeError, match="Input must be a list"): + flatten_array(123) + +def test_invalid_input_non_integer_element(): + """Test that a TypeError is raised for non-integer/non-list elements""" + with pytest.raises(TypeError): + flatten_array([1, 2, "3"]) + with pytest.raises(TypeError): + flatten_array([1, [2, 3.14], 4]) + +def test_flatten_single_element_list(): + """Test flattening a list with a single element""" + assert flatten_array([42]) == [42] + +def test_flatten_list_with_single_nested_list(): + """Test flattening a list containing a single nested list""" + assert flatten_array([[1]]) == [1] \ No newline at end of file From 1b9cd9538767f96cc994b679f522e4c922610892 Mon Sep 17 00:00:00 2001 From: labrocadabro Date: Tue, 22 Apr 2025 09:51:52 -0300 Subject: [PATCH 09/19] Implement binary search function with comprehensive error handling --- src/binary_search.py | 46 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 src/binary_search.py diff --git a/src/binary_search.py b/src/binary_search.py new file mode 100644 index 00000000..abbb6ed1 --- /dev/null +++ b/src/binary_search.py @@ -0,0 +1,46 @@ +def binary_search(arr, target): + """ + Perform binary search on a sorted array to find the target element. + + Args: + arr (list): A sorted list of comparable elements (ascending order) + target: The element to search for + + Returns: + int: Index of the target element if found, -1 otherwise + + Raises: + TypeError: If input is not a list + ValueError: If the input list is not sorted + """ + # Validate input + if not isinstance(arr, list): + raise TypeError("Input must be a list") + + # Check if list is sorted + if arr != sorted(arr): + raise ValueError("Input list must be sorted in ascending order") + + # Edge case: empty list + if not arr: + return -1 + + # Binary search implementation + left, right = 0, len(arr) - 1 + + while left <= right: + # Prevent potential integer overflow + mid = left + (right - left) // 2 + + # Check if target is found + if arr[mid] == target: + return mid + + # Decide which half to search + if arr[mid] < target: + left = mid + 1 + else: + right = mid - 1 + + # Target not found + return -1 \ No newline at end of file From 6b19ee331ea780be02f2174b8eca439b4df422e7 Mon Sep 17 00:00:00 2001 From: labrocadabro Date: Tue, 22 Apr 2025 09:52:04 -0300 Subject: [PATCH 10/19] Add comprehensive tests for binary search function --- tests/test_binary_search.py | 49 +++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 tests/test_binary_search.py diff --git a/tests/test_binary_search.py b/tests/test_binary_search.py new file mode 100644 index 00000000..4f35522e --- /dev/null +++ b/tests/test_binary_search.py @@ -0,0 +1,49 @@ +import pytest +from src.binary_search import binary_search + +def test_binary_search_normal_case(): + """Test binary search with a typical sorted list.""" + arr = [1, 3, 5, 7, 9, 11, 13] + assert binary_search(arr, 7) == 3 + assert binary_search(arr, 13) == 6 + assert binary_search(arr, 1) == 0 + +def test_binary_search_not_found(): + """Test when target is not in the list.""" + arr = [1, 3, 5, 7, 9, 11, 13] + assert binary_search(arr, 4) == -1 + assert binary_search(arr, 0) == -1 + assert binary_search(arr, 14) == -1 + +def test_binary_search_empty_list(): + """Test binary search on an empty list.""" + arr = [] + assert binary_search(arr, 5) == -1 + +def test_binary_search_single_element(): + """Test binary search on a single-element list.""" + arr = [5] + assert binary_search(arr, 5) == 0 + assert binary_search(arr, 6) == -1 + +def test_binary_search_invalid_input(): + """Test error handling for invalid inputs.""" + with pytest.raises(TypeError): + binary_search("not a list", 5) + + with pytest.raises(ValueError): + binary_search([5, 3, 1], 3) # Unsorted list + +def test_binary_search_duplicate_elements(): + """Test binary search with duplicate elements.""" + arr = [1, 2, 2, 3, 3, 3, 4, 4, 5] + # Note: This returns the index of one of the duplicate elements + assert binary_search(arr, 3) in [4, 5, 6] + assert binary_search(arr, 2) in [1, 2] + +def test_binary_search_large_list(): + """Test binary search on a larger sorted list.""" + arr = list(range(1000)) + assert binary_search(arr, 500) == 500 + assert binary_search(arr, 999) == 999 + assert binary_search(arr, 1000) == -1 \ No newline at end of file From c07d50f191ceb818a5946c385e4f6b051a8f88a5 Mon Sep 17 00:00:00 2001 From: laura-abro Date: Tue, 22 Apr 2025 09:53:34 -0300 Subject: [PATCH 11/19] Implement URL parser function with comprehensive parsing --- src/url_parser.py | 50 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 src/url_parser.py diff --git a/src/url_parser.py b/src/url_parser.py new file mode 100644 index 00000000..7be1f397 --- /dev/null +++ b/src/url_parser.py @@ -0,0 +1,50 @@ +from urllib.parse import urlparse, parse_qs +from typing import Dict, Any + +def parse_url(url: str) -> Dict[str, Any]: + """ + Parse a given URL into its component parts. + + Args: + url (str): The URL to be parsed. + + Returns: + Dict[str, Any]: A dictionary containing parsed URL components: + - protocol: The URL scheme (e.g., 'http', 'https') + - domain: The domain name + - port: The port number (or None if not specified) + - path: The path component of the URL + - query_params: A dictionary of query parameters + - fragment: The fragment identifier (or None if not present) + + Raises: + ValueError: If the input is not a valid URL string. + """ + # Validate input + if not isinstance(url, str): + raise ValueError("Input must be a string") + + # Handle empty or whitespace-only strings + if not url.strip(): + raise ValueError("URL cannot be empty") + + try: + # Use urlparse to break down the URL + parsed_url = urlparse(url) + + # Extract query parameters + query_params = parse_qs(parsed_url.query) + # Convert query params to their single values if possible + query_params = {k: v[0] if len(v) == 1 else v for k, v in query_params.items()} + + # Construct the result dictionary + return { + 'protocol': parsed_url.scheme or None, + 'domain': parsed_url.hostname or None, + 'port': parsed_url.port, + 'path': parsed_url.path or None, + 'query_params': query_params, + 'fragment': parsed_url.fragment or None + } + except Exception: + raise ValueError(f"Invalid URL: {url}") \ No newline at end of file From dcd6082dbe3a496ea0afe656891964e6e5a75264 Mon Sep 17 00:00:00 2001 From: laura-abro Date: Tue, 22 Apr 2025 09:53:50 -0300 Subject: [PATCH 12/19] Add comprehensive tests for URL parser function --- tests/test_url_parser.py | 83 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 tests/test_url_parser.py diff --git a/tests/test_url_parser.py b/tests/test_url_parser.py new file mode 100644 index 00000000..a8c26628 --- /dev/null +++ b/tests/test_url_parser.py @@ -0,0 +1,83 @@ +import pytest +from src.url_parser import parse_url + +def test_complete_url(): + """Test parsing a complete URL with all components.""" + url = "https://www.example.com:8080/path/to/page?key1=value1&key2=value2#section" + result = parse_url(url) + + assert result == { + 'protocol': 'https', + 'domain': 'www.example.com', + 'port': 8080, + 'path': '/path/to/page', + 'query_params': {'key1': 'value1', 'key2': 'value2'}, + 'fragment': 'section' + } + +def test_minimal_url(): + """Test parsing a minimal URL with just protocol and domain.""" + url = "http://example.com" + result = parse_url(url) + + assert result == { + 'protocol': 'http', + 'domain': 'example.com', + 'port': None, + 'path': None, + 'query_params': {}, + 'fragment': None + } + +def test_url_with_multiple_query_params(): + """Test URL with multiple query parameters.""" + url = "https://example.com/search?q=test&category=books&sort=relevance" + result = parse_url(url) + + assert result == { + 'protocol': 'https', + 'domain': 'example.com', + 'port': None, + 'path': '/search', + 'query_params': { + 'q': 'test', + 'category': 'books', + 'sort': 'relevance' + }, + 'fragment': None + } + +def test_url_with_no_protocol(): + """Test URL without a protocol.""" + url = "example.com/path" + result = parse_url(url) + + assert result == { + 'protocol': None, + 'domain': 'example.com', + 'port': None, + 'path': '/path', + 'query_params': {}, + 'fragment': None + } + +def test_invalid_input_types(): + """Test error handling for invalid input types.""" + with pytest.raises(ValueError, match="Input must be a string"): + parse_url(123) + + with pytest.raises(ValueError, match="Input must be a string"): + parse_url(None) + +def test_empty_string(): + """Test error handling for empty or whitespace-only strings.""" + with pytest.raises(ValueError, match="URL cannot be empty"): + parse_url("") + + with pytest.raises(ValueError, match="URL cannot be empty"): + parse_url(" ") + +def test_invalid_url(): + """Test error handling for clearly invalid URLs.""" + with pytest.raises(ValueError, match="Invalid URL"): + parse_url("not a valid url") \ No newline at end of file From 78ce04a8c7b1459d8b72267f1cc0fc8f72f68100 Mon Sep 17 00:00:00 2001 From: laura-abro Date: Tue, 22 Apr 2025 09:53:59 -0300 Subject: [PATCH 13/19] Add pytest to requirements --- requirements.txt | 1 + 1 file changed, 1 insertion(+) create mode 100644 requirements.txt diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 00000000..55b033e9 --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +pytest \ No newline at end of file From 26a42f3791a1503e6e4000e9cd41d7e8285dd815 Mon Sep 17 00:00:00 2001 From: laura-abro Date: Tue, 22 Apr 2025 09:54:25 -0300 Subject: [PATCH 14/19] Update URL parser to handle more edge cases --- src/url_parser.py | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/url_parser.py b/src/url_parser.py index 7be1f397..8c97cc86 100644 --- a/src/url_parser.py +++ b/src/url_parser.py @@ -29,6 +29,10 @@ def parse_url(url: str) -> Dict[str, Any]: raise ValueError("URL cannot be empty") try: + # If no protocol is present, try adding a default one + if '://' not in url: + url = 'http://' + url + # Use urlparse to break down the URL parsed_url = urlparse(url) @@ -37,12 +41,24 @@ def parse_url(url: str) -> Dict[str, Any]: # Convert query params to their single values if possible query_params = {k: v[0] if len(v) == 1 else v for k, v in query_params.items()} + # Determine domain and path + domain = parsed_url.hostname + path = parsed_url.path if parsed_url.path and parsed_url.path != '/' else None + + # Ensure path starts with '/' if present + if path and not path.startswith('/'): + path = '/' + path + + # Validate the URL against minimum requirements + if not domain and not path: + raise ValueError(f"Invalid URL: {url}") + # Construct the result dictionary return { 'protocol': parsed_url.scheme or None, - 'domain': parsed_url.hostname or None, + 'domain': domain, 'port': parsed_url.port, - 'path': parsed_url.path or None, + 'path': path, 'query_params': query_params, 'fragment': parsed_url.fragment or None } From 6bc4d717c9448e12083887cb9cf05baa968ad28d Mon Sep 17 00:00:00 2001 From: laura-abro Date: Tue, 22 Apr 2025 09:54:57 -0300 Subject: [PATCH 15/19] Further refine URL parser to handle more edge cases --- src/url_parser.py | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/src/url_parser.py b/src/url_parser.py index 8c97cc86..82ba5940 100644 --- a/src/url_parser.py +++ b/src/url_parser.py @@ -29,34 +29,38 @@ def parse_url(url: str) -> Dict[str, Any]: raise ValueError("URL cannot be empty") try: - # If no protocol is present, try adding a default one - if '://' not in url: - url = 'http://' + url + # Validate basic structure of the URL + if not any(char in url for char in ['/', '.', ':']): + raise ValueError(f"Invalid URL: {url}") - # Use urlparse to break down the URL + # Try parsing with no protocol first parsed_url = urlparse(url) + # If no hostname found, try adding http:// + if not parsed_url.hostname: + # If no protocol and no hostname, this looks like an invalid URL + url = 'http://' + url + parsed_url = urlparse(url) + # Extract query parameters query_params = parse_qs(parsed_url.query) # Convert query params to their single values if possible query_params = {k: v[0] if len(v) == 1 else v for k, v in query_params.items()} - # Determine domain and path - domain = parsed_url.hostname + # Determine protocol + protocol = parsed_url.scheme if parsed_url.scheme != 'http' else None + + # Determine path path = parsed_url.path if parsed_url.path and parsed_url.path != '/' else None # Ensure path starts with '/' if present if path and not path.startswith('/'): path = '/' + path - # Validate the URL against minimum requirements - if not domain and not path: - raise ValueError(f"Invalid URL: {url}") - # Construct the result dictionary return { - 'protocol': parsed_url.scheme or None, - 'domain': domain, + 'protocol': protocol, + 'domain': parsed_url.hostname, 'port': parsed_url.port, 'path': path, 'query_params': query_params, From 9a1efb08a0457a2acefb0d22552ea2a25e631064 Mon Sep 17 00:00:00 2001 From: laura-abro Date: Tue, 22 Apr 2025 09:55:27 -0300 Subject: [PATCH 16/19] Final refinement of URL parser to pass all tests --- src/url_parser.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/url_parser.py b/src/url_parser.py index 82ba5940..85447e21 100644 --- a/src/url_parser.py +++ b/src/url_parser.py @@ -47,7 +47,7 @@ def parse_url(url: str) -> Dict[str, Any]: # Convert query params to their single values if possible query_params = {k: v[0] if len(v) == 1 else v for k, v in query_params.items()} - # Determine protocol + # Determine protocol - keep as 'http' if that's the default protocol = parsed_url.scheme if parsed_url.scheme != 'http' else None # Determine path @@ -59,7 +59,7 @@ def parse_url(url: str) -> Dict[str, Any]: # Construct the result dictionary return { - 'protocol': protocol, + 'protocol': parsed_url.scheme, 'domain': parsed_url.hostname, 'port': parsed_url.port, 'path': path, From c58f240df8cc70ff681a7f4e0f41d14bb9deb0eb Mon Sep 17 00:00:00 2001 From: laura-abro Date: Tue, 22 Apr 2025 09:55:59 -0300 Subject: [PATCH 17/19] Final implementation of URL parser to handle all test cases --- src/url_parser.py | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/src/url_parser.py b/src/url_parser.py index 85447e21..2b4957ab 100644 --- a/src/url_parser.py +++ b/src/url_parser.py @@ -33,23 +33,27 @@ def parse_url(url: str) -> Dict[str, Any]: if not any(char in url for char in ['/', '.', ':']): raise ValueError(f"Invalid URL: {url}") - # Try parsing with no protocol first - parsed_url = urlparse(url) - - # If no hostname found, try adding http:// - if not parsed_url.hostname: - # If no protocol and no hostname, this looks like an invalid URL - url = 'http://' + url + # Try parsing with potential manual protocol handling + if '://' not in url: + # If the URL contains path, treat it as a potential http URL + if '/' in url: + # If there's a path, try http:// + parsed_url = urlparse('http://' + url) + protocol = None + else: + # If just a domain, try http:// + parsed_url = urlparse('http://' + url) + protocol = None + else: + # If protocol is present, use as-is parsed_url = urlparse(url) + protocol = parsed_url.scheme # Extract query parameters query_params = parse_qs(parsed_url.query) # Convert query params to their single values if possible query_params = {k: v[0] if len(v) == 1 else v for k, v in query_params.items()} - # Determine protocol - keep as 'http' if that's the default - protocol = parsed_url.scheme if parsed_url.scheme != 'http' else None - # Determine path path = parsed_url.path if parsed_url.path and parsed_url.path != '/' else None @@ -59,7 +63,7 @@ def parse_url(url: str) -> Dict[str, Any]: # Construct the result dictionary return { - 'protocol': parsed_url.scheme, + 'protocol': protocol, 'domain': parsed_url.hostname, 'port': parsed_url.port, 'path': path, From 694cd4bb3ecd4c8da21e38239d12d145fa8d4838 Mon Sep 17 00:00:00 2001 From: labrocadabro Date: Tue, 22 Apr 2025 09:58:31 -0300 Subject: [PATCH 18/19] Add RGB to Hex converter function --- src/rgb_to_hex.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 src/rgb_to_hex.py diff --git a/src/rgb_to_hex.py b/src/rgb_to_hex.py new file mode 100644 index 00000000..eccce1b6 --- /dev/null +++ b/src/rgb_to_hex.py @@ -0,0 +1,24 @@ +def rgb_to_hex(r: int, g: int, b: int) -> str: + """ + Convert RGB color values to a hexadecimal color representation. + + Args: + r (int): Red color value (0-255) + g (int): Green color value (0-255) + b (int): Blue color value (0-255) + + Returns: + str: Hexadecimal color representation (e.g., '#FF0000') + + Raises: + ValueError: If any color value is outside the valid range of 0-255 + """ + # Validate input values + for color, name in [(r, 'Red'), (g, 'Green'), (b, 'Blue')]: + if not isinstance(color, int): + raise TypeError(f"{name} value must be an integer") + if color < 0 or color > 255: + raise ValueError(f"{name} value must be between 0 and 255") + + # Convert RGB to hex, ensuring two-digit representation + return f'#{r:02X}{g:02X}{b:02X}' \ No newline at end of file From e9d45b0a253d521b376ac878efa2ef5e9a06093e Mon Sep 17 00:00:00 2001 From: labrocadabro Date: Tue, 22 Apr 2025 09:58:46 -0300 Subject: [PATCH 19/19] Add comprehensive tests for RGB to Hex converter --- tests/test_rgb_to_hex.py | 53 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 tests/test_rgb_to_hex.py diff --git a/tests/test_rgb_to_hex.py b/tests/test_rgb_to_hex.py new file mode 100644 index 00000000..3cde58df --- /dev/null +++ b/tests/test_rgb_to_hex.py @@ -0,0 +1,53 @@ +import pytest +from src.rgb_to_hex import rgb_to_hex + +def test_rgb_to_hex_basic(): + """Test basic color conversion""" + assert rgb_to_hex(255, 0, 0) == '#FF0000' # Red + assert rgb_to_hex(0, 255, 0) == '#00FF00' # Green + assert rgb_to_hex(0, 0, 255) == '#0000FF' # Blue + assert rgb_to_hex(255, 255, 255) == '#FFFFFF' # White + assert rgb_to_hex(0, 0, 0) == '#000000' # Black + +def test_rgb_to_hex_mixed_colors(): + """Test mixed color conversions""" + assert rgb_to_hex(128, 128, 128) == '#808080' # Gray + assert rgb_to_hex(255, 165, 0) == '#FFA500' # Orange + +def test_rgb_to_hex_boundary_values(): + """Test boundary values""" + assert rgb_to_hex(0, 0, 0) == '#000000' + assert rgb_to_hex(255, 255, 255) == '#FFFFFF' + +def test_rgb_to_hex_invalid_inputs(): + """Test error handling for invalid inputs""" + # Test negative values + with pytest.raises(ValueError, match="Red value must be between 0 and 255"): + rgb_to_hex(-1, 0, 0) + + with pytest.raises(ValueError, match="Green value must be between 0 and 255"): + rgb_to_hex(0, -1, 0) + + with pytest.raises(ValueError, match="Blue value must be between 0 and 255"): + rgb_to_hex(0, 0, -1) + + # Test values over 255 + with pytest.raises(ValueError, match="Red value must be between 0 and 255"): + rgb_to_hex(256, 0, 0) + + with pytest.raises(ValueError, match="Green value must be between 0 and 255"): + rgb_to_hex(0, 256, 0) + + with pytest.raises(ValueError, match="Blue value must be between 0 and 255"): + rgb_to_hex(0, 0, 256) + +def test_rgb_to_hex_type_errors(): + """Test type checking""" + with pytest.raises(TypeError, match="Red value must be an integer"): + rgb_to_hex('255', 0, 0) + + with pytest.raises(TypeError, match="Green value must be an integer"): + rgb_to_hex(0, '255', 0) + + with pytest.raises(TypeError, match="Blue value must be an integer"): + rgb_to_hex(0, 0, '255') \ No newline at end of file