diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 8d94a35..0c28d5b 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -13,4 +13,4 @@ updates: - package-ecosystem: "github-actions" # See documentation for possible values directory: "/" # Location of package manifests schedule: - interval: "weekly" + interval: "monthly" diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 74910e5..7de447e 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -24,7 +24,7 @@ jobs: uses: astral-sh/setup-uv@v4 - name: Setup Python - run: uv python install + run: uv python install 3.9 - name: Install project & build run: uv sync --all-extras && uv build @@ -33,4 +33,4 @@ jobs: uses: actions/upload-artifact@v4 with: path: dist/* - retention-days: 10 + retention-days: 3 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1c2adf8..068ea65 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -23,7 +23,7 @@ jobs: uses: astral-sh/setup-uv@v4 - name: Setup Python - run: uv python install + run: uv python install 3.9 - name: Install project & build run: uv sync --all-extras && uv build diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index 2534578..ec6d81c 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -27,7 +27,7 @@ jobs: uses: astral-sh/setup-uv@v4 - name: Setup Python - run: uv python install + run: uv python install 3.9 - name: Install project run: uv sync --all-extras --dev diff --git a/.python-version b/.python-version deleted file mode 100644 index 9432908..0000000 --- a/.python-version +++ /dev/null @@ -1 +0,0 @@ -3.9.20 diff --git a/README.md b/README.md index 1ee2303..5cc7d39 100644 --- a/README.md +++ b/README.md @@ -61,27 +61,28 @@ This small tour guide will describe how you can use avro.py back and forth to op #### 1. `parse()` -Let's assume I want to parse some English text to Bengali, which is "ami banglay gan gai.", so in this case to convert it to Bengali, we can use this snippet: +Let's assume I want to parse some English text to Bengali, which is "ami banglay gan gai.", so in this case to convert it to Bengali, we can use this starter code and follow along with the other examples to add further features: ```python -# Import the package. +import asyncio import avro -# Our dummy text. -dummy = 'ami banglay gan gai.' +async def main() -> None: + dummy = 'ami banglay gan gai.' -# Parsing the text. -avro_output = avro.parse(dummy) -print(output) # Output: আমি বাংলায় গান গাই। + avro_output = await avro.parse(dummy) + print(output) # Output: আমি বাংলায় গান গাই। + +if __name__ == '__main__': + asyncio.run(main()) ``` #### 2. `parse(bijoy=True)` -Alternatively, I can also do it in Bijoy Keyboard format: +Alternatively, I can also do it in **Bijoy Keyboard format**: ```python -# Parsing in Bijoy. -bijoy_output = avro.parse(dummy, bijoy=True) # Output: Avwg evsjvh় Mvb MvB। +bijoy_output = await avro.parse(dummy, bijoy=True) # Output: Avwg evsjvh় Mvb MvB। ``` #### 3. `to_bijoy()` @@ -89,8 +90,7 @@ bijoy_output = avro.parse(dummy, bijoy=True) # Output: Avwg evsjvh় Mvb MvB Or, we can take the previous `avro_output` and convert it to Bijoy if we want to, like this: ```python -# Converting to Bijoy. -bijoy_text = avro.to_bijoy(avro_output) # Output: Avwg evsjvh় Mvb MvB। +bijoy_text = await avro.to_bijoy(avro_output) # Output: Avwg evsjvh় Mvb MvB। ``` #### 4. `to_unicode()` @@ -98,8 +98,7 @@ bijoy_text = avro.to_bijoy(avro_output) # Output: Avwg evsjvh় Mvb MvB। Conversely, we can convert the Bijoy text we got just now and convert it back to Unicode Bengali: ```python -# Converting back! -unicode_text = avro.to_unicode(bijoy_text) # Output: আমি বাংলায় গান গাই। +unicode_text = await avro.to_unicode(bijoy_text) # Output: আমি বাংলায় গান গাই। ``` #### 4. `reverse()` @@ -107,8 +106,7 @@ unicode_text = avro.to_unicode(bijoy_text) # Output: আমি বাংলা Finally, we can just reverse back to the original text we passed as input in the first place: ```python -# Reversing back! -reversed_text = avro.reverse(uncode_text) # Output: ami banglay gan gai. +reversed_text = await avro.reverse(uncode_text) # Output: ami banglay gan gai. ```
diff --git a/REFERENCES.md b/REFERENCES.md new file mode 100644 index 0000000..f972865 --- /dev/null +++ b/REFERENCES.md @@ -0,0 +1,5 @@ +## Important References / Links +Note: This file has only been created for storing important resources related to the development of the project. + +- NumPy-themed documentation (Sphinx-compatible): https://numpydoc.readthedocs.io/en/latest/format.html +- Projects concept (uv): https://docs.astral.sh/uv/concepts/projects/init/ diff --git a/assets/banner.png b/assets/banner.png index 0ce3c02..cb93d47 100644 Binary files a/assets/banner.png and b/assets/banner.png differ diff --git a/examples/simple.py b/examples/simple.py index 11150e7..15fa14a 100644 --- a/examples/simple.py +++ b/examples/simple.py @@ -1,28 +1,34 @@ -# Import the package. +# Imports. +import asyncio + import avro -# Let's assume we'd like to convert "amar sOnar bangla" to Unicode. -dummy_text = "amar sOnar bangla" -print("Original English Text:", dummy_text) -# Parse the text to Avro. -parsed_text = avro.parse(dummy_text) -print("Parsed Unicode Output:", parsed_text) +# Please follow the detailed guidelines given in the README.md file about each function to learn more. +async def main() -> None: + dummy_text = "amar sOnar bangla" # Dummy text. + print("Original English Text:", dummy_text) + + parsed_text = await avro.parse(dummy_text) # Parse the text to Bengali. + print("Parsed Unicode Output:", parsed_text) + + # We can parse it directly to Bijoy, or use the to_bijoy function to convert it. + # Both should return the same result. + bijoy_text_direct = await avro.parse(dummy_text, bijoy=True) + bijoy_text_function = await avro.to_bijoy(parsed_text) + + if bijoy_text_direct == bijoy_text_function: + print(f"Bijoy Output: {bijoy_text_direct}") -# We can parse it directly to Bijoy, or use the to_bijoy function to convert it. -# Both should return the same result. -bijoy_text_direct = avro.parse(dummy_text, bijoy=True) -bijoy_text_function = avro.to_bijoy(parsed_text) + # Now, we can return the Bijoy text to Unicode since we'd like the original output (assuming). + # This should be the same as the old parsed_text variable. + unicode_text = await avro.to_unicode(bijoy_text_direct) + print("Reversed Unicode Output:", unicode_text) -# Print the Bijoy text. -if bijoy_text_direct == bijoy_text_function: - print(f"Bijoy Output: {bijoy_text_direct}") + # Finally, we can reverse the Bengali text, back to English! + reversed = await avro.reverse(unicode_text) + print("Reversed English Output:", reversed) -# Now, we can return the Bijoy text to Unicode since we'd like the original output (assuming). -# This should be the same as the old parsed_text variable. -unicode_text = avro.to_unicode(bijoy_text_direct) -print("Reversed Unicode Output:", unicode_text) -# Finally, we can reverse the Bengali text, back to English! -reversed = avro.reverse(unicode_text) -print("Reversed English Output:", reversed) +if __name__ == "__main__": + asyncio.run(main()) diff --git a/pyproject.toml b/pyproject.toml index 531217e..1cfd894 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ # uv-specific things [project] name = "avro-py" -version = "2024.10.30" +version = "2024.12.5" description = "A modern Pythonic implementation of Avro Phonetic." readme = "README.md" license = { file = "LICENSE" } @@ -31,6 +31,7 @@ classifiers = [ 'Programming Language :: Python :: 3.13', 'Topic :: Software Development :: Libraries :: Python Modules', ] +dependencies = [] # project links @@ -42,14 +43,14 @@ Repository = "https://github.com/hitblast/avro.py" # dependency groups [dependency-groups] -dev = ["pytest>=8.3.3", "ruff>=0.7.1"] +dev = ["pytest-asyncio>=0.24.0", "pytest>=8.3.3", "ruff>=0.7.1"] docs = ["pdoc3>=0.11.1", "setuptools>=75.3.0"] # ruff-specific things [tool.ruff] target-version = "py39" -line-length = 110 +line-length = 79 exclude = ["venv"] [tool.ruff.lint.per-file-ignores] diff --git a/pytest.ini b/pytest.ini new file mode 100644 index 0000000..2f4c80e --- /dev/null +++ b/pytest.ini @@ -0,0 +1,2 @@ +[pytest] +asyncio_mode = auto diff --git a/src/avro/core/config.py b/src/avro/core/config.py index 609816c..6f726a4 100755 --- a/src/avro/core/config.py +++ b/src/avro/core/config.py @@ -1,7 +1,7 @@ # SPDX-License-Identifier: MIT -# Import local modules. +# Imports. from ..resources import DICT # Shortcuts to vowels, constants, case-sensitives and numbers. diff --git a/src/avro/core/count.py b/src/avro/core/count.py index 1aeaa58..a9e385f 100755 --- a/src/avro/core/count.py +++ b/src/avro/core/count.py @@ -1,22 +1,44 @@ # SPDX-License-Identifier: MIT -# Import local modules. +# Imports. from avro.core import config # Functions. def count_vowels(text: str) -> int: - """ - Count number of occurrences of vowels in a given string. + """Count number of occurrences of vowels in a given string. + + Parameters: + ----------- + + text: str + The text to count vowels from. + + Returns: + -------- + + int + The number of vowels in the given text. """ return sum(1 for i in text if i.lower() in config.AVRO_VOWELS) def count_consonants(text: str) -> int: - """ - Count number of occurrences of consonants in a given string. + """Count number of occurrences of consonants in a given string. + + Parameters: + ----------- + + text: str + The text to count consonants from. + + Returns: + -------- + + int + The number of consonants in the given text. """ return sum(1 for i in text if i.lower() in config.AVRO_CONSONANTS) diff --git a/src/avro/core/objects.py b/src/avro/core/objects.py deleted file mode 100644 index bd6da97..0000000 --- a/src/avro/core/objects.py +++ /dev/null @@ -1 +0,0 @@ -# TODO: add a custom string object representation for the entire project diff --git a/src/avro/core/processor.py b/src/avro/core/processor.py index 7c30685..7fc9c94 100644 --- a/src/avro/core/processor.py +++ b/src/avro/core/processor.py @@ -1,12 +1,11 @@ # SPDX-License-Identifier: MIT -# Import first-party Python libraries. +# Imports. import contextlib from functools import lru_cache from typing import Any, Optional -# Import local modules. from . import config, validate # Setup pattern variables for matching. @@ -15,31 +14,56 @@ RULE_PATTERNS = [p for p in PATTERNS if "rules" in p] +# --- + # The following functions provide the functionality for the main "avro" package. # These include rule / non-rule based pattern matching, remapping, reversing and more. # Please do not modify these functions if you do not have the intentions to alter the core functionality of the module. +# --- + @lru_cache(maxsize=128) def find_in_remap(text: str, *, reversed: bool = False) -> tuple[str, bool]: - """ - Finds and returns the remapped value for a given text. + """Finds and returns the remapped value for a given text. + + Parameters: + ----------- + + text: str + The text to be remapped. - Returns a tuple of two elements: - - (`str`): The remapped text. - - (`bool`) Whether manual intervention is required. + reversed: bool + Whether to operate in reverse mode. + + Returns: + -------- + + tuple[str, bool] + A tuple of two elements: + 1. The remapped text. + 2. Whether manual intervention is required. """ previous_text = text for key, value in config.AVRO_EXCEPTIONS.items(): if reversed: - text = text.replace(key, value) if key.lower() in text.lower() else text + text = ( + text.replace(key, value) + if key.lower() in text.lower() + else text + ) else: - text = text.replace(value, key) if (value := value.lower()) in text.lower() else text + text = ( + text.replace(value, key) + if (value := value.lower()) in text.lower() + else text + ) manual_required = any( - word == previous_word for word, previous_word in zip(text.split(), previous_text.split()) + word == previous_word + for word, previous_word in zip(text.split(), previous_text.split()) ) return text, manual_required @@ -48,10 +72,33 @@ def find_in_remap(text: str, *, reversed: bool = False) -> tuple[str, bool]: def match_patterns( fixed_text: str, cur: int = 0, rule: bool = False, reversed: bool = False ) -> dict[str, Any]: - """ - Matches given text at cursor position with rule / non rule patterns. + """Matches given text at cursor position with rule / non rule patterns. + + Parameters: + ----------- + + fixed_text: str + The text to be matched. + + cur: int + The cursor position. - Returns a dictionary of three (upto four) elements. + rule: bool + Whether to match with rules. + + reversed: bool + Whether to operate in reverse mode. + + Returns: + -------- + + dict[str, Any] + A dictionary containing the following keys: + 1. matched: bool + 2. found: str + 3. replaced: str + 4. reversed: str + 5. rules: dict[str, Any] """ rule_type = NON_RULE_PATTERNS if not rule else RULE_PATTERNS @@ -64,7 +111,9 @@ def match_patterns( "matched": True, "found": p.get("find"), "replaced": p.get("replace"), - "reversed": reverse_with_rules(cur, fixed_text, p.get("reverse")) if not rule else None, + "reversed": reverse_with_rules(cur, fixed_text, p.get("reverse")) + if not rule + else None, "rules": p.get("rules") if rule else None, } @@ -79,8 +128,28 @@ def match_patterns( def exact_find_in_pattern( fixed_text: str, reversed: bool, cur: int = 0, patterns: Any = PATTERNS ) -> list[dict[str, Any]]: - """ - Returns pattern items that match given text, cursor position and pattern. + """Returns pattern items that match given text, cursor position and pattern. + + Parameters: + ----------- + + fixed_text: str + The text to be matched. + + reversed: bool + Whether to operate in reverse mode. + + cur: int = 0 + The cursor position. + + patterns: Any = PATTERNS + The patterns to be matched. + + Returns: + -------- + + list[dict[str, Any]] + A list of dictionaries containing the matched patterns. """ if reversed: @@ -100,9 +169,28 @@ def exact_find_in_pattern( ] -def reverse_with_rules(cursor: int, fixed_text: str, text_reversed: str) -> str: - """ - Enhances the word with rules for reverse-parsing. +def reverse_with_rules( + cursor: int, fixed_text: str, text_reversed: str +) -> str: + """Enhances the word with rules for reverse-parsing. + + Parameters: + ----------- + + cursor: int + The cursor position. + + fixed_text: str + The fixed text. + + text_reversed: str + The reversed text. + + Returns: + -------- + + str + The enhanced word. """ added_suffix = "" @@ -124,12 +212,33 @@ def reverse_with_rules(cursor: int, fixed_text: str, text_reversed: str) -> str: return text_reversed if not text_reversed else text_reversed + added_suffix -def process_rules(rules: dict[str, Any], fixed_text: str, cur: int = 0, cur_end: int = 1) -> Optional[str]: - """ - Process rules matched in pattern and returns suitable replacement. +def process_rules( + rules: dict[str, Any], fixed_text: str, cur: int = 0, cur_end: int = 1 +) -> Optional[str]: + """Process rules matched in pattern and returns suitable replacement. + + If any rule's condition is satisfied, output the rules "replace", else output None. + + Parameters: + ----------- - If any rule's condition is satisfied, output the rules "replace", - else output None. + rules: dict[str, Any] + The rules to be processed. + + fixed_text: str + The fixed text. + + cur: int = 0 + The cursor position. + + cur_end: int = 1 + The end cursor position. + + Returns: + -------- + + Optional[str] + The replaced text if any rule's condition is satisfied, else None. """ replaced = "" @@ -152,8 +261,28 @@ def process_rules(rules: dict[str, Any], fixed_text: str, cur: int = 0, cur_end: def process_match(match: Any, fixed_text: str, cur: int, cur_end: int) -> bool: - """ - Processes a single match in rules. + """Processes a single match in rules. + + Parameters: + ----------- + + match: Any + The match to be processed. + + fixed_text: str + The fixed text. + + cur: int + The cursor position. + + cur_end: int + The end cursor position. + + Returns: + -------- + + bool + True if the match is successful, else False. """ # Initial/default value for replace. @@ -216,17 +345,28 @@ def process_match(match: Any, fixed_text: str, cur: int, cur_end: int) -> bool: exact_start = cur_end exact_end = cur_end + len(match["value"]) - if not validate.is_exact(match["value"], fixed_text, exact_start, exact_end, negative): + if not validate.is_exact( + match["value"], fixed_text, exact_start, exact_end, negative + ): replace = False return replace def rearrange_unicode_text(text: str) -> str: - """ - Rearranges Unicode (Avro) text to match conversion standards for ASCII. + """Rearranges Unicode (Avro) text to match conversion standards for ASCII. + + Parameters: + ----------- + + text: str + The text to be rearranged. - Returns the rearranged string. + Returns: + -------- + + str + The rearranged text. """ # Convert the string to a list of individual characters. @@ -268,7 +408,12 @@ def rearrange_unicode_text(text: str) -> str: else: break - chars[i - 1], chars[i], chars[i + 1 : i + j + found_pre_kar + 1], chars[i + j + 1 :] = ( + ( + chars[i - 1], + chars[i], + chars[i + 1 : i + j + found_pre_kar + 1], + chars[i + j + 1 :], + ) = ( chars[i + j + 1], chars[i + 1 : i + j + 1], chars[i - 1], @@ -282,20 +427,38 @@ def rearrange_unicode_text(text: str) -> str: def rearrange_bijoy_text(text: str) -> str: - """ - Rearranges Bijoy Keyboard text to match conversion standards for Unicode. + """Rearranges Bijoy Keyboard text to match conversion standards for Unicode. + + Parameters: + ----------- - Returns the rearranged string. + text: str + The text to be rearranged. + + Returns: + -------- + + str + The rearranged text. """ i = 0 while i < len(text): if ( i > 0 and text[i] == "\u09cd" - and (validate.is_bangla_kar(text[i - 1]) or validate.is_bangla_nukta(text[i - 1])) + and ( + validate.is_bangla_kar(text[i - 1]) + or validate.is_bangla_nukta(text[i - 1]) + ) and i < len(text) - 1 ): - text = text[: i - 1] + text[i] + text[i + 1] + text[i - 1] + text[i + 2 :] + text = ( + text[: i - 1] + + text[i] + + text[i + 1] + + text[i - 1] + + text[i + 2 :] + ) if ( 0 < i < len(text) - 1 @@ -304,7 +467,13 @@ def rearrange_bijoy_text(text: str) -> str: and text[i - 2] != "\u09cd" and validate.is_bangla_kar(text[i + 1]) ): - text = text[: i - 1] + text[i + 1] + text[i - 1] + text[i] + text[i + 2 :] + text = ( + text[: i - 1] + + text[i + 1] + + text[i - 1] + + text[i] + + text[i + 2 :] + ) if ( i < len(text) - 1 @@ -316,17 +485,29 @@ def rearrange_bijoy_text(text: str) -> str: while True: if i - j < 0: break - if validate.is_bangla_banjonborno(text[i - j]) and validate.is_bangla_halant(text[i - j - 1]): + if validate.is_bangla_banjonborno( + text[i - j] + ) and validate.is_bangla_halant(text[i - j - 1]): j += 2 elif j == 1 and validate.is_bangla_kar(text[i - j]): j += 1 else: break - text = text[: i - j] + text[i] + text[i + 1] + text[i - j : i] + text[i + 2 :] + text = ( + text[: i - j] + + text[i] + + text[i + 1] + + text[i - j : i] + + text[i + 2 :] + ) i += 1 continue - if i < len(text) - 1 and validate.is_bangla_prekar(text[i]) and text[i + 1] != " ": + if ( + i < len(text) - 1 + and validate.is_bangla_prekar(text[i]) + and text[i + 1] != " " + ): j = 1 while validate.is_bangla_banjonborno(text[i + j]): if validate.is_bangla_halant((part := text[i + j + 1])): @@ -347,7 +528,11 @@ def rearrange_bijoy_text(text: str) -> str: text = temp i += j - if i < len(text) - 1 and text[i] == "ঁ" and validate.is_bangla_postkar(text[i + 1]): + if ( + i < len(text) - 1 + and text[i] == "ঁ" + and validate.is_bangla_postkar(text[i + 1]) + ): temp = text[:i] + text[i + 1] + text[i] + text[i + 2 :] text = temp diff --git a/src/avro/core/validate.py b/src/avro/core/validate.py index c3f6d2e..91258ba 100755 --- a/src/avro/core/validate.py +++ b/src/avro/core/validate.py @@ -36,7 +36,10 @@ def is_punctuation(text: str) -> bool: Check if given string is a punctuation. """ - return not (text.lower() in config.AVRO_VOWELS or text.lower() in config.AVRO_CONSONANTS) + return not ( + text.lower() in config.AVRO_VOWELS + or text.lower() in config.AVRO_CONSONANTS + ) def is_case_sensitive(text: str) -> bool: @@ -47,12 +50,16 @@ def is_case_sensitive(text: str) -> bool: return text.lower() in config.AVRO_CASESENSITIVES -def is_exact(needle: str, haystack: str, start: int, end: int, matchnot: bool) -> bool: +def is_exact( + needle: str, haystack: str, start: int, end: int, matchnot: bool +) -> bool: """ Check exact occurrence of needle in haystack. """ - return (start >= 0 and end < len(haystack) and haystack[start:end] == needle) != matchnot + return ( + start >= 0 and end < len(haystack) and haystack[start:end] == needle + ) != matchnot def fix_string_case(text: str) -> str: diff --git a/src/avro/main.py b/src/avro/main.py index 9c2f331..d0381a8 100755 --- a/src/avro/main.py +++ b/src/avro/main.py @@ -1,50 +1,78 @@ # SPDX-License-Identifier: MIT -""" -The primary module for the avro.py package. +"""The core module for the avro.py package. +avro.py Licensed under the terms of the MIT License. """ -# Import first-party Python libraries. +# Imports. +import asyncio import re -from concurrent.futures import ThreadPoolExecutor, as_completed from functools import lru_cache from itertools import chain from typing import Callable, Generator, Union -# Import local modules. from .core import processor, validate from .core.config import BIJOY_MAP, BIJOY_MAP_REVERSE +# Compiled regex patterns. These are primarily used in parse() and reverse() +# function calls to validate input text and search for invalid UTF-8 characters. +UTF8_REGEX = re.compile(r"\A[\x00-\x7F]*\Z", re.UNICODE) +REVERSE_REGEX = re.compile(r"(\s|\.|,|\?|।|-|;|')", re.UNICODE) -# Concurrency helper function for handling multithreaded workloads. -def _concurrency_helper(func: Callable, params: tuple[str, ...]) -> list[str]: - output = [] - with ThreadPoolExecutor() as executor: - futures = {executor.submit(func, text): text for text in params} +# This is a backend function and MUST NOT BE EXPORTED! +async def _async_concurrency_helper( + func: Callable, params: tuple[str, ...] +) -> list[str]: + """Concurrency helper for the core functions of avro.py. - for future in as_completed(futures): - output.append(future.result()) + Parameters: + ----------- - return output + func: Callable + The function to run concurrently. + params: tuple[str, ...] + The parameters to pass to the function. -# Compiled regular expression for UTF-8 validation. -UTF8_REGEX = re.compile(r"\A[\x00-\x7F]*\Z", re.UNICODE) + Returns: + -------- -# Compiled regular expression for removing noise while reversing. -REVERSE_REGEX = re.compile(r"(\s|\.|,|\?|।|-|;|')", re.UNICODE) + list[str] + The results of the function run concurrently. + """ + loop = asyncio.get_event_loop() + tasks = [loop.run_in_executor(None, func, text) for text in params] + results = await asyncio.gather(*tasks) + return results -# --- +# This is a backend function and MUST NOT BE EXPORTED! +def _parse_output_generator( + fixed_text: str, cur_end: int +) -> Generator[str, None, None]: + """Output generator for the parse() function. + + Parameters: + ----------- + + fixed_text: str + The fixed text to parse. + + cur_end: int + The cursor end point. + + Yields: + ------- + + str + The parsed output. + """ -# Backend Functions. -# The output generator for the parse function. -def _parse_output_generator(fixed_text: str, cur_end: int) -> Generator[str, None, None]: for cur, i in enumerate(fixed_text): uni_pass = UTF8_REGEX.match(i) is not None if not uni_pass: @@ -62,7 +90,10 @@ def _parse_output_generator(fixed_text: str, cur_end: int) -> Generator[str, Non if matched: cur_end = cur + len(match["found"]) replaced = processor.process_rules( - rules=match["rules"], fixed_text=fixed_text, cur=cur, cur_end=cur_end + rules=match["rules"], + fixed_text=fixed_text, + cur=cur, + cur_end=cur_end, ) if replaced: yield replaced @@ -73,28 +104,65 @@ def _parse_output_generator(fixed_text: str, cur_end: int) -> Generator[str, Non yield i -# The working backend for the parse() function. +# This is a backend function and MUST NOT BE EXPORTED! @lru_cache(maxsize=128) def _parse_backend(text: str, remap_words: bool) -> str: - fixed_text = validate.fix_string_case(text) # Sanitize input text. - manual_required = True # Whether manual intervention is required. - cur_end = 0 # Cursor end point. + """The working backend for the parse() function. + + Parameters: + ----------- + + text: str + The text to parse. + + remap_words: bool + Whether to parse input text with remapped (exception) words. + + Returns: + -------- + + str + The parsed text. + """ + + fixed_text = validate.fix_string_case(text) + manual_required = True + cur_end = 0 # Replace predefined exceptions in the input text. if remap_words: fixed_text, manual_required = processor.find_in_remap(fixed_text) return ( - "".join(chain.from_iterable(_parse_output_generator(fixed_text, cur_end))) + "".join( + chain.from_iterable(_parse_output_generator(fixed_text, cur_end)) + ) if manual_required else fixed_text ) -# The working backend for the to_bijoy() function. +# This is a backend function and MUST NOT BE EXPORTED! @lru_cache(maxsize=128) def _convert_backend(text: str) -> str: - text = processor.rearrange_unicode_text(re.sub("ৌ", "ৌ", re.sub("ো", "ো", text))) + """The working backend for the to_bijoy() function. + + Parameters: + ----------- + + text: str + The text to convert. + + Returns: + -------- + + str + The converted text. + """ + + text = processor.rearrange_unicode_text( + re.sub("ৌ", "ৌ", re.sub("ো", "ো", text)) + ) for unic in BIJOY_MAP: text = re.sub(unic, BIJOY_MAP[unic], text) @@ -102,9 +170,24 @@ def _convert_backend(text: str) -> str: return text.strip() -# The working backend for the to_unicode() function. +# This is a backend function and MUST NOT BE EXPORTED! @lru_cache(maxsize=128) def _convert_backend_unicode(text: str) -> str: + """The working backend for the to_unicode() function. + + Parameters: + ----------- + + text: str + The text to convert. + + Returns: + -------- + + str + The converted text. + """ + for ascii_c in BIJOY_MAP_REVERSE: text = re.sub(re.escape(ascii_c), BIJOY_MAP_REVERSE[ascii_c], text) @@ -112,149 +195,312 @@ def _convert_backend_unicode(text: str) -> str: return text.strip() -# The output generator for the reverse function. +# This is a backend function and MUST NOT BE EXPORTED! +def _reverse_output_generator(text: str) -> Generator[str, None, None]: + """ + Generates the reversed output for the reverse() function. + + Parameters: + ----------- + text: str + The text to reverse. + + Yields: + ------- + str + The reversed output. + """ + + for cur, i in enumerate(text): + try: + i.encode("utf-8") + match = processor.match_patterns( + text, cur, rule=False, reversed=True + ) + yield ( + (match["reversed"] or match["found"]) + if match["matched"] + else i + ) + except UnicodeDecodeError: + yield i + + +# This is a backend function and MUST NOT BE EXPORTED! +@lru_cache(maxsize=128) +def _reverse_backend(text: str, remap_words: bool) -> str: + """ + The working backend for the reverse() function. + + Parameters: + ----------- + text: str + The text to reverse. + + remap_words: bool + Whether to reverse input text with remapped (exception) words. + + Returns: + -------- + str + The reversed text. + """ + + manual_required = True + + if remap_words: + text, manual_required = processor.find_in_remap(text, reversed=True) + + return ( + "".join(chain.from_iterable(_reverse_output_generator(text))) + if manual_required + else text + ) + + +# This is a backend function and MUST NOT BE EXPORTED! +@lru_cache(maxsize=128) +def _reverse_backend_ext(text: str, remap_words: bool) -> str: + """ + Backend extension for the reverse() function. + + Parameters: + ----------- + text: str + The text to reverse. + + remap_words: bool + Whether to reverse input text with remapped (exception) words. + + Returns: + -------- + str + The reversed text. + """ + + separated_texts = REVERSE_REGEX.split(text) + text_segments = [ + _reverse_backend(separated_text, remap_words) + for separated_text in separated_texts + ] + return "".join(text_segments) + + +# This is a backend function and MUST NOT BE EXPORTED! def _reverse_output_generator(text: str) -> Generator[str, None, None]: + """The output generator for the reverse() function. + + Parameters: + ----------- + + text: str + The text to reverse. + + Yields: + ------- + + str + The reversed output. + """ + for cur, i in enumerate(text): try: i.encode("utf-8") - match = processor.match_patterns(text, cur, rule=False, reversed=True) - yield (match["reversed"] or match["found"]) if match["matched"] else i + match = processor.match_patterns( + text, cur, rule=False, reversed=True + ) + yield ( + (match["reversed"] or match["found"]) + if match["matched"] + else i + ) except UnicodeDecodeError: yield i -# The working backend for the reverse() function. +# This is a backend function and MUST NOT BE EXPORTED! @lru_cache(maxsize=128) def _reverse_backend(text: str, remap_words: bool) -> str: + """The working backend for the reverse() function. + + Parameters: + ----------- + + text: str + The text to reverse. + + remap_words: bool + Whether to reverse input text with remapped (exception) words. + + Returns: + -------- + + str + The reversed text. + """ + manual_required = True # Whether manual intervention is required. # Replace predefined exceptions in the input text. if remap_words: text, manual_required = processor.find_in_remap(text, reversed=True) - return "".join(chain.from_iterable(_reverse_output_generator(text))) if manual_required else text + return ( + "".join(chain.from_iterable(_reverse_output_generator(text))) + if manual_required + else text + ) -# Backend extension for the reverse() function. +# This is a backend function and MUST NOT BE EXPORTED! @lru_cache(maxsize=128) def _reverse_backend_ext(text: str, remap_words: bool) -> str: + """Backend extension for the reverse() function. + + Parameters: + ----------- + + text: str + The text to reverse. + + remap_words: bool + Whether to reverse input text with remapped (exception) words. + + Returns: + -------- + + str + The reversed text. + """ + separated_texts = REVERSE_REGEX.split(text) - text_segments = [_reverse_backend(separated_text, remap_words) for separated_text in separated_texts] + text_segments = [ + _reverse_backend(separated_text, remap_words) + for separated_text in separated_texts + ] return "".join(text_segments) # --- - # Primary user-end functions. -# The parse() function. -# Used to parse from English Roman script to Bengali in Unicode. -def parse(*texts: str, bijoy: bool = False, remap_words: bool = True) -> Union[str, list[str]]: - """ - #### Parses input text, matches and replaces using the Avro Dictionary. + +# --- + + +async def parse( + *texts: str, bijoy: bool = False, remap_words: bool = True +) -> Union[str, list[str]]: + """Parses input text, matches and replaces using the Avro Dictionary. If a valid replacement is found, then it returns the replaced string. If no replacement is found, then it instead returns the input text. Parameters: - - `*texts: str | tuple[str]`: The text(s) to parse. - - `bijoy: bool = False`: Return result in the Bijoy Keyboard format (ASCII). - - `remap_words: bool = True`: Whether to parse input text with remapped (exception) words. + ----------- + *texts: str + The text(s) to parse. + + bijoy: bool = False + Whether to return result in the Bijoy Keyboard format (ASCII). - Usage: - ```python - import avro + remap_words: bool = True + Whether to parse input text with remapped (exception) words. - parsed = avro.parse('ami banglay gan gai') - print(parsed) - ``` + Returns: + -------- + + str | list[str] + The parsed text(s). """ - output = _concurrency_helper(lambda text: _parse_backend(text, remap_words), texts) + output = await _async_concurrency_helper( + lambda text: _parse_backend(text, remap_words), texts + ) # If the `bijoy` parameter is set to `True`, then convert the output to Bijoy Keyboard format. if bijoy: - return to_bijoy(*output) + return await to_bijoy(*output) else: return output[0] if len(output) == 1 else output -# The to_bijoy() function. -# Used to parse from Bengali in Unicode to Bijoy Keyboard format. -def to_bijoy(*texts: str) -> Union[str, list[str]]: - """ - #### Converts input text (Avro, Unicode) to Bijoy Keyboard format (ASCII). +async def to_bijoy(*texts: str) -> Union[str, list[str]]: + """Converts input text (Avro, Unicode) to Bijoy Keyboard format (ASCII). If a valid conversion is found, then it returns the converted string. Parameters: - - `*texts: str | Tuple[str]`: The text(s) to convert. + ----------- + + *texts: str + The text(s) to convert. - Usage: - ```python - import avro + Returns: + -------- - converted = avro.to_bijoy('আমার সোনার বাংলা') - print(converted) - ``` + str | list[str] + The converted text(s). """ - output = _concurrency_helper(_convert_backend, texts) + output = await _async_concurrency_helper(_convert_backend, texts) return output[0] if len(output) == 1 else output -# The to_unicode() function. -# Used to parse from Bijoy Keyboard format to Bengali in Unicode. -def to_unicode(*texts: str) -> Union[str, list[str]]: - """ - #### Converts input text (Bijoy Keyboard, ASCII) to Unicode (Avro Keyboard format). +async def to_unicode(*texts: str) -> Union[str, list[str]]: + """Converts input text (Bijoy Keyboard, ASCII) to Unicode (Avro Keyboard format). If a valid conversion is found, then it returns the converted string. Parameters: - - `*texts: str | Tuple[str]`: The text(s) to convert. - - Usage: - ```python - import avro - - converted = avro.to_unicode('Avwg evsjvh় Mvb MvB;') - print(converted) - ``` + ----------- + *texts: str + The text(s) to convert. + + Returns: + -------- + str | list[str] + The converted text(s). """ - output = _concurrency_helper(_convert_backend_unicode, texts) + output = await _async_concurrency_helper(_convert_backend_unicode, texts) return output[0] if len(output) == 1 else output -# The reverse() function. -# Used to parse from Bengali in Unicode to English Roman script. -def reverse(*texts: str, from_bijoy: bool = False, remap_words: bool = True) -> Union[str, list[str]]: - """ - #### Reverses input text to Roman script typed in English. +async def reverse( + *texts: str, from_bijoy: bool = False, remap_words: bool = True +) -> Union[str, list[str]]: + """Reverses input text to Roman script typed in English. If a valid replacement is found, then it returns the replaced string. If no replacement is found, then it instead returns the input text. Parameters: - - `*texts: str | Tuple[str]`: The text(s) to reverse. - - `from_bijoy: bool = False`: Reverse text from Bijoy format (ASCII) directly. - - `remap_words: bool = True`: Whether to reverse input text with remapped (exception) words. + ----------- - Usage: - ```python - import avro + *texts: str + The text(s) to reverse. - reversed = avro.reverse('আমার সোনার বাংলা') - print(reversed) - ``` + from_bijoy: bool = False + Whether to reverse input text from Bijoy Keyboard format (ASCII). + + remap_words: bool = True + Whether to reverse input text with remapped (exception) words. + + Returns: + -------- + + str | list[str] + The reversed text(s). """ # Convert from Bijoy to Unicode if from_bijoy is True if from_bijoy: - converted_texts = to_unicode(*texts) + converted_texts = await to_unicode(*texts) if isinstance(converted_texts, str): texts = (converted_texts,) - output = _concurrency_helper(lambda text: _reverse_backend_ext(text, remap_words), texts) + output = await _async_concurrency_helper( + lambda text: _reverse_backend_ext(text, remap_words), texts + ) return output[0] if len(output) == 1 else output diff --git a/src/avro/resources/dictionary.py b/src/avro/resources/dictionary.py index c6e846f..1f4d8fc 100644 --- a/src/avro/resources/dictionary.py +++ b/src/avro/resources/dictionary.py @@ -1,7 +1,7 @@ # SPDX-License-Identifier: MIT -# Import first-party Python modules. +# Imports. from typing import Any # The Avro Dictionary, implemented in Python. @@ -201,8 +201,16 @@ "find": "OI", "replace": "ৈ", "rules": [ - {"matches": [{"type": "prefix", "scope": "!consonant"}], "replace": "ঐ"}, - {"matches": [{"type": "prefix", "scope": "punctuation"}], "replace": "ঐ"}, + { + "matches": [{"type": "prefix", "scope": "!consonant"}], + "replace": "ঐ", + }, + { + "matches": [ + {"type": "prefix", "scope": "punctuation"} + ], + "replace": "ঐ", + }, ], "reverse": "oi", }, @@ -210,8 +218,16 @@ "find": "OU", "replace": "ৌ", "rules": [ - {"matches": [{"type": "prefix", "scope": "!consonant"}], "replace": "ঔ"}, - {"matches": [{"type": "prefix", "scope": "punctuation"}], "replace": "ঔ"}, + { + "matches": [{"type": "prefix", "scope": "!consonant"}], + "replace": "ঔ", + }, + { + "matches": [ + {"type": "prefix", "scope": "punctuation"} + ], + "replace": "ঔ", + }, ], "reverse": "ou", }, @@ -219,8 +235,16 @@ "find": "O", "replace": "ো", "rules": [ - {"matches": [{"type": "prefix", "scope": "!consonant"}], "replace": "ও"}, - {"matches": [{"type": "prefix", "scope": "punctuation"}], "replace": "ও"}, + { + "matches": [{"type": "prefix", "scope": "!consonant"}], + "replace": "ও", + }, + { + "matches": [ + {"type": "prefix", "scope": "punctuation"} + ], + "replace": "ও", + }, ], "reverse": "o", }, @@ -240,8 +264,16 @@ "find": "rri", "replace": "ৃ", "rules": [ - {"matches": [{"type": "prefix", "scope": "!consonant"}], "replace": "ঋ"}, - {"matches": [{"type": "prefix", "scope": "punctuation"}], "replace": "ঋ"}, + { + "matches": [{"type": "prefix", "scope": "!consonant"}], + "replace": "ঋ", + }, + { + "matches": [ + {"type": "prefix", "scope": "punctuation"} + ], + "replace": "ঋ", + }, ], "reverse": "ri", }, @@ -254,10 +286,26 @@ { "matches": [ {"type": "prefix", "scope": "consonant"}, - {"type": "prefix", "scope": "!exact", "value": "r"}, - {"type": "prefix", "scope": "!exact", "value": "y"}, - {"type": "prefix", "scope": "!exact", "value": "w"}, - {"type": "prefix", "scope": "!exact", "value": "x"}, + { + "type": "prefix", + "scope": "!exact", + "value": "r", + }, + { + "type": "prefix", + "scope": "!exact", + "value": "y", + }, + { + "type": "prefix", + "scope": "!exact", + "value": "w", + }, + { + "type": "prefix", + "scope": "!exact", + "value": "x", + }, ], "replace": "্র্য", } @@ -270,10 +318,26 @@ { "matches": [ {"type": "prefix", "scope": "consonant"}, - {"type": "prefix", "scope": "!exact", "value": "r"}, - {"type": "prefix", "scope": "!exact", "value": "y"}, - {"type": "prefix", "scope": "!exact", "value": "w"}, - {"type": "prefix", "scope": "!exact", "value": "x"}, + { + "type": "prefix", + "scope": "!exact", + "value": "r", + }, + { + "type": "prefix", + "scope": "!exact", + "value": "y", + }, + { + "type": "prefix", + "scope": "!exact", + "value": "w", + }, + { + "type": "prefix", + "scope": "!exact", + "value": "x", + }, ], "replace": "্র্য", } @@ -287,7 +351,11 @@ "matches": [ {"type": "prefix", "scope": "!consonant"}, {"type": "suffix", "scope": "!vowel"}, - {"type": "suffix", "scope": "!exact", "value": "r"}, + { + "type": "suffix", + "scope": "!exact", + "value": "r", + }, {"type": "suffix", "scope": "!punctuation"}, ], "replace": "র্", @@ -295,7 +363,11 @@ { "matches": [ {"type": "prefix", "scope": "consonant"}, - {"type": "prefix", "scope": "!exact", "value": "r"}, + { + "type": "prefix", + "scope": "!exact", + "value": "r", + }, ], "replace": "্রর", }, @@ -311,11 +383,31 @@ { "matches": [ {"type": "prefix", "scope": "consonant"}, - {"type": "prefix", "scope": "!exact", "value": "r"}, - {"type": "prefix", "scope": "!exact", "value": "y"}, - {"type": "prefix", "scope": "!exact", "value": "w"}, - {"type": "prefix", "scope": "!exact", "value": "x"}, - {"type": "prefix", "scope": "!exact", "value": "Z"}, + { + "type": "prefix", + "scope": "!exact", + "value": "r", + }, + { + "type": "prefix", + "scope": "!exact", + "value": "y", + }, + { + "type": "prefix", + "scope": "!exact", + "value": "w", + }, + { + "type": "prefix", + "scope": "!exact", + "value": "x", + }, + { + "type": "prefix", + "scope": "!exact", + "value": "Z", + }, ], "replace": "্র", } @@ -367,14 +459,22 @@ { "matches": [ {"type": "prefix", "scope": "!consonant"}, - {"type": "suffix", "scope": "!exact", "value": "`"}, + { + "type": "suffix", + "scope": "!exact", + "value": "`", + }, ], "replace": "উ", }, { "matches": [ {"type": "prefix", "scope": "punctuation"}, - {"type": "suffix", "scope": "!exact", "value": "`"}, + { + "type": "suffix", + "scope": "!exact", + "value": "`", + }, ], "replace": "উ", }, @@ -393,7 +493,11 @@ { "matches": [ {"type": "prefix", "scope": "vowel"}, - {"type": "prefix", "scope": "!exact", "value": "o"}, + { + "type": "prefix", + "scope": "!exact", + "value": "o", + }, ], "replace": "ও", }, @@ -404,7 +508,12 @@ ], "replace": "অ", }, - {"matches": [{"type": "prefix", "scope": "punctuation"}], "replace": "অ"}, + { + "matches": [ + {"type": "prefix", "scope": "punctuation"} + ], + "replace": "অ", + }, ], }, {"find": "tth", "replace": "ত্থ"}, @@ -431,22 +540,38 @@ { "matches": [ {"type": "prefix", "scope": "punctuation"}, - {"type": "suffix", "scope": "!exact", "value": "`"}, + { + "type": "suffix", + "scope": "!exact", + "value": "`", + }, ], "replace": "আ", }, { "matches": [ {"type": "prefix", "scope": "!consonant"}, - {"type": "prefix", "scope": "!exact", "value": "a"}, - {"type": "suffix", "scope": "!exact", "value": "`"}, + { + "type": "prefix", + "scope": "!exact", + "value": "a", + }, + { + "type": "suffix", + "scope": "!exact", + "value": "`", + }, ], "replace": "য়া", }, { "matches": [ {"type": "prefix", "scope": "exact", "value": "a"}, - {"type": "suffix", "scope": "!exact", "value": "`"}, + { + "type": "suffix", + "scope": "!exact", + "value": "`", + }, ], "replace": "আ", }, @@ -462,14 +587,22 @@ { "matches": [ {"type": "prefix", "scope": "!consonant"}, - {"type": "suffix", "scope": "!exact", "value": "`"}, + { + "type": "suffix", + "scope": "!exact", + "value": "`", + }, ], "replace": "ই", }, { "matches": [ {"type": "prefix", "scope": "punctuation"}, - {"type": "suffix", "scope": "!exact", "value": "`"}, + { + "type": "suffix", + "scope": "!exact", + "value": "`", + }, ], "replace": "ই", }, @@ -483,14 +616,22 @@ { "matches": [ {"type": "prefix", "scope": "!consonant"}, - {"type": "suffix", "scope": "!exact", "value": "`"}, + { + "type": "suffix", + "scope": "!exact", + "value": "`", + }, ], "replace": "ঈ", }, { "matches": [ {"type": "prefix", "scope": "punctuation"}, - {"type": "suffix", "scope": "!exact", "value": "`"}, + { + "type": "suffix", + "scope": "!exact", + "value": "`", + }, ], "replace": "ঈ", }, @@ -506,14 +647,22 @@ { "matches": [ {"type": "prefix", "scope": "!consonant"}, - {"type": "suffix", "scope": "!exact", "value": "`"}, + { + "type": "suffix", + "scope": "!exact", + "value": "`", + }, ], "replace": "উ", }, { "matches": [ {"type": "prefix", "scope": "punctuation"}, - {"type": "suffix", "scope": "!exact", "value": "`"}, + { + "type": "suffix", + "scope": "!exact", + "value": "`", + }, ], "replace": "উ", }, @@ -528,14 +677,22 @@ { "matches": [ {"type": "prefix", "scope": "!consonant"}, - {"type": "suffix", "scope": "!exact", "value": "`"}, + { + "type": "suffix", + "scope": "!exact", + "value": "`", + }, ], "replace": "ঊ", }, { "matches": [ {"type": "prefix", "scope": "punctuation"}, - {"type": "suffix", "scope": "!exact", "value": "`"}, + { + "type": "suffix", + "scope": "!exact", + "value": "`", + }, ], "replace": "ঊ", }, @@ -550,14 +707,22 @@ { "matches": [ {"type": "prefix", "scope": "!consonant"}, - {"type": "suffix", "scope": "!exact", "value": "`"}, + { + "type": "suffix", + "scope": "!exact", + "value": "`", + }, ], "replace": "ঈ", }, { "matches": [ {"type": "prefix", "scope": "punctuation"}, - {"type": "suffix", "scope": "!exact", "value": "`"}, + { + "type": "suffix", + "scope": "!exact", + "value": "`", + }, ], "replace": "ঈ", }, @@ -574,14 +739,22 @@ { "matches": [ {"type": "prefix", "scope": "!consonant"}, - {"type": "suffix", "scope": "!exact", "value": "`"}, + { + "type": "suffix", + "scope": "!exact", + "value": "`", + }, ], "replace": "এ", }, { "matches": [ {"type": "prefix", "scope": "punctuation"}, - {"type": "suffix", "scope": "!exact", "value": "`"}, + { + "type": "suffix", + "scope": "!exact", + "value": "`", + }, ], "replace": "এ", }, @@ -601,7 +774,12 @@ ], "replace": "য়", }, - {"matches": [{"type": "prefix", "scope": "punctuation"}], "replace": "ইয়"}, + { + "matches": [ + {"type": "prefix", "scope": "punctuation"} + ], + "replace": "ইয়", + }, ], }, {"find": "Y", "replace": "য়", "reverse": "y"}, @@ -617,14 +795,24 @@ ], "replace": "ওয়", }, - {"matches": [{"type": "prefix", "scope": "consonant"}], "replace": "্ব"}, + { + "matches": [{"type": "prefix", "scope": "consonant"}], + "replace": "্ব", + }, ], "reverse": "o", }, { "find": "x", "replace": "ক্স", - "rules": [{"matches": [{"type": "prefix", "scope": "punctuation"}], "replace": "এক্স"}], + "rules": [ + { + "matches": [ + {"type": "prefix", "scope": "punctuation"} + ], + "replace": "এক্স", + } + ], "reverse": "ks", }, {"find": ":`", "replace": ":"}, diff --git a/tests/test_main.py b/tests/test_main.py index ccdd882..8f7e31c 100755 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -7,25 +7,31 @@ from typing import NoReturn # Add support layer for accessing the primary package. -sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir))) +sys.path.append( + os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir)) +) # Import local modules. +import pytest + import avro from avro.core import config # Test functions for this file. -def test_patterns_without_rules_from_config() -> NoReturn: +@pytest.mark.asyncio +async def test_patterns_without_rules_from_config() -> NoReturn: """ Tests all patterns from config that don't have rules. """ for pattern in config.DICT["avro"]["patterns"]: if "rules" not in pattern and pattern.get("find", None): - assert pattern["replace"] == avro.parse(pattern["find"]) + assert pattern["replace"] == await avro.parse(pattern["find"]) -def test_patterns_without_rules_not_from_config() -> NoReturn: +@pytest.mark.asyncio +async def test_patterns_without_rules_not_from_config() -> NoReturn: """ Tests all patterns not from config that don't have rules. @@ -43,10 +49,11 @@ def test_patterns_without_rules_not_from_config() -> NoReturn: } for key, value in conjunctions.items(): - assert key == avro.parse(value) + assert key == await avro.parse(value) -def test_patterns_numbers() -> NoReturn: +@pytest.mark.asyncio +async def test_patterns_numbers() -> NoReturn: """ Test patterns - numbers """ @@ -66,10 +73,11 @@ def test_patterns_numbers() -> NoReturn: } for key, value in numbers.items(): - assert key == avro.parse(value) + assert key == await avro.parse(value) -def test_patterns_punctuations() -> NoReturn: +@pytest.mark.asyncio +async def test_patterns_punctuations() -> NoReturn: """ Tests patterns - punctuations """ @@ -81,11 +89,12 @@ def test_patterns_punctuations() -> NoReturn: } for key, value in punctuations.items(): - assert key == avro.parse(value) - assert value == avro.reverse(key) + assert key == await avro.parse(value) + assert value == await avro.reverse(key) -def test_patterns_with_rules_svaravarna() -> NoReturn: +@pytest.mark.asyncio +async def test_patterns_with_rules_svaravarna() -> NoReturn: """ Test patterns - with rules - svaravarna / shoroborno (derived from Bengali) """ @@ -105,10 +114,11 @@ def test_patterns_with_rules_svaravarna() -> NoReturn: } for key, value in svaravarna.items(): - assert key == avro.parse(value) + assert key == await avro.parse(value) -def test_non_ascii() -> NoReturn: +@pytest.mark.asyncio +async def test_non_ascii() -> NoReturn: """ Test processor response for non-ASCII characters. Parse function should return any non-ASCII characters that is passed to it. @@ -123,22 +133,25 @@ def test_non_ascii() -> NoReturn: } for key, value in non_ascii.items(): - assert key == avro.parse(value) + assert key == await avro.parse(value) -def test_ascii() -> NoReturn: +@pytest.mark.asyncio +async def test_ascii() -> NoReturn: """ Test processor response for ASCII characters. Reverse function should return any ASCII characters that is passed to it. """ - assert "Avwg evsjvi gan MvB|" == avro.reverse("Avwg evsjvi গান MvB|") - assert "Avwg amar Avwg‡K wPiw`b GB banglay Lyu‡R cvB!" == avro.reverse( - "Avwg আমার Avwg‡K wPiw`b GB বাংলায় Lyu‡R cvB!" + assert "Avwg evsjvi gan MvB|" == await avro.reverse("Avwg evsjvi গান MvB|") + assert ( + "Avwg amar Avwg‡K wPiw`b GB banglay Lyu‡R cvB!" + == await avro.reverse("Avwg আমার Avwg‡K wPiw`b GB বাংলায় Lyu‡R cvB!") ) -def test_words_with_punctuations() -> NoReturn: +@pytest.mark.asyncio +async def test_words_with_punctuations() -> NoReturn: """ Test parsing and reversing of words with punctuations. """ @@ -151,79 +164,104 @@ def test_words_with_punctuations() -> NoReturn: } for key, value in test_words.items(): - assert key == avro.parse(value) - assert value.lower() == avro.reverse(key) + assert key == await avro.parse(value) + assert value.lower() == await avro.reverse(key) -def test_exceptions() -> NoReturn: +@pytest.mark.asyncio +async def test_exceptions() -> NoReturn: """ Test parsing and reversing of exceptions. """ - assert "আমি উইকিপিডিয়া আর ফেসবুক চালাই।" == avro.parse("ami Wikipedia ar Facebook calai.") - assert "ami Wikipedia ar Facebook chalai." == avro.reverse("আমি উইকিপিডিয়া আর ফেসবুক চালাই।") + assert "আমি উইকিপিডিয়া আর ফেসবুক চালাই।" == await avro.parse( + "ami Wikipedia ar Facebook calai." + ) + assert "ami Wikipedia ar Facebook chalai." == await avro.reverse( + "আমি উইকিপিডিয়া আর ফেসবুক চালাই।" + ) -def test_conversion_bijoy_func() -> NoReturn: +@pytest.mark.asyncio +async def test_conversion_bijoy_func() -> NoReturn: """ Test conversion to Bijoy directly. """ # Regular Conversion. - assert "Avwg evsjvq Mvb MvB;" == avro.to_bijoy("আমি বাংলায় গান গাই;") + assert "Avwg evsjvq Mvb MvB;" == await avro.to_bijoy("আমি বাংলায় গান গাই;") assert [ "Avwg evsjvi Mvb MvB|", "Avwg Avgvi Avwg‡K wPiw`b GB evsjvq Lyu‡R cvB!", - ] == avro.to_bijoy("আমি বাংলার গান গাই।", "আমি আমার আমিকে চিরদিন এই বাংলায় খুঁজে পাই!") + ] == await avro.to_bijoy( + "আমি বাংলার গান গাই।", "আমি আমার আমিকে চিরদিন এই বাংলায় খুঁজে পাই!" + ) # Fail-safe Conversion. - assert "Hello, World!" == avro.to_bijoy("Hello, World!") + assert "Hello, World!" == await avro.to_bijoy("Hello, World!") -def test_conversion_unicode_func() -> NoReturn: +@pytest.mark.asyncio +async def test_conversion_unicode_func() -> NoReturn: """ Test conversion to Unicode directly. """ # Regular Conversion. - assert "আমি বাংলায় গান গাই;" == avro.to_unicode("Avwg evsjvh় Mvb MvB;") + assert "আমি বাংলায় গান গাই;" == await avro.to_unicode("Avwg evsjvh় Mvb MvB;") assert [ "আমি বাংলার গান গাই।", "আমি আমার আমিকে চিরদিন এই বাংলায় খুঁজে পাই!", - ] == avro.to_unicode("Avwg evsjvi Mvb MvB|", "Avwg Avgvi Avwg‡K wPiw`b GB evsjvq Lyu‡R cvB!") + ] == await avro.to_unicode( + "Avwg evsjvi Mvb MvB|", "Avwg Avgvi Avwg‡K wPiw`b GB evsjvq Lyu‡R cvB!" + ) -def test_parse_sentences() -> NoReturn: +@pytest.mark.asyncio +async def test_parse_sentences() -> NoReturn: """ Test parsing of sentences (Unicode). """ # Default parsing. - assert "আমি বাংলায় গান গাই;" == avro.parse("ami banglay gan gai;") + assert "আমি বাংলায় গান গাই;" == await avro.parse("ami banglay gan gai;") assert [ "আমি বাংলার গান গাই।", "আমি আমার আমিকে চিরদিন এই বাংলায় খুঁজে পাই।", - ] == avro.parse("ami banglar gan gai.", "ami amar amike cirodin ei banglay khu^je pai.") + ] == await avro.parse( + "ami banglar gan gai.", "ami amar amike cirodin ei banglay khu^je pai." + ) # Bijoy parsing. - assert "Avwg evsjvq Mvb MvB;" == avro.parse("ami banglay gan gai;", bijoy=True) + assert "Avwg evsjvq Mvb MvB;" == await avro.parse( + "ami banglay gan gai;", bijoy=True + ) assert [ "Avwg evsjvi Mvb MvB|", "Avwg Avgvi Avwg‡K wPiw`b GB evsjvq Lyu‡R cvB!", - ] == avro.parse("ami banglar gan gai.", "ami amar amike cirodin ei banglay khu^je pai!", bijoy=True) + ] == await avro.parse( + "ami banglar gan gai.", + "ami amar amike cirodin ei banglay khu^je pai!", + bijoy=True, + ) -def test_reverse_sentences() -> NoReturn: +@pytest.mark.asyncio +async def test_reverse_sentences() -> NoReturn: """ Test reversing of sentences (Unicode). """ # Default reversing. - assert "ami banglay gan gai." == avro.reverse("আমি বাংলায় গান গাই।") + assert "ami banglay gan gai." == await avro.reverse("আমি বাংলায় গান গাই।") assert [ "rohim, tomake korim dakche. ekhon ki rowna debe?", "rowna dile amake bole zew.", - ] == avro.reverse("রহিম, তোমাকে করিম ডাকছে। এখন কি রওনা দেবে?", "রওনা দিলে আমাকে বলে যেও।") + ] == await avro.reverse( + "রহিম, তোমাকে করিম ডাকছে। এখন কি রওনা দেবে?", "রওনা দিলে আমাকে বলে যেও।" + ) # Bijoy reversing. - assert "ami banglar gan gai." == avro.reverse("Avwg evsjvi Mvb MvB|", from_bijoy=True) + assert "ami banglar gan gai." == await avro.reverse( + "Avwg evsjvi Mvb MvB|", from_bijoy=True + ) diff --git a/tests/test_utils_count.py b/tests/test_utils_count.py index 7be924c..b41eb3a 100755 --- a/tests/test_utils_count.py +++ b/tests/test_utils_count.py @@ -7,7 +7,9 @@ from typing import NoReturn # Add support layer for accessing the primary package. -sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir))) +sys.path.append( + os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir)) +) # Import local modules. from avro.core import count diff --git a/tests/test_utils_validate.py b/tests/test_utils_validate.py index ca9f3b3..0ac2a27 100755 --- a/tests/test_utils_validate.py +++ b/tests/test_utils_validate.py @@ -7,7 +7,9 @@ from typing import NoReturn # Add support layer for accessing the primary package. -sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir))) +sys.path.append( + os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir)) +) # Import local modules. from avro.core import validate diff --git a/uv.lock b/uv.lock index cb9e37b..502f8fe 100644 --- a/uv.lock +++ b/uv.lock @@ -3,12 +3,13 @@ requires-python = ">=3.9" [[package]] name = "avro-py" -version = "2024.10.30" +version = "2024.12.5" source = { editable = "." } [package.dev-dependencies] dev = [ { name = "pytest" }, + { name = "pytest-asyncio" }, { name = "ruff" }, ] docs = [ @@ -21,6 +22,7 @@ docs = [ [package.metadata.requires-dev] dev = [ { name = "pytest", specifier = ">=8.3.3" }, + { name = "pytest-asyncio", specifier = ">=0.24.0" }, { name = "ruff", specifier = ">=0.7.1" }, ] docs = [ @@ -69,14 +71,14 @@ wheels = [ [[package]] name = "mako" -version = "1.3.6" +version = "1.3.7" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "markupsafe" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/fa/0b/29bc5a230948bf209d3ed3165006d257e547c02c3c2a96f6286320dfe8dc/mako-1.3.6.tar.gz", hash = "sha256:9ec3a1583713479fae654f83ed9fa8c9a4c16b7bb0daba0e6bbebff50c0d983d", size = 390206 } +sdist = { url = "https://files.pythonhosted.org/packages/5a/27/5af876b41cebd9d76fa8333b83ef9121726893f725952022edd194a1671e/mako-1.3.7.tar.gz", hash = "sha256:20405b1232e0759f0e7d87b01f6bb94fce0761747f1cb876ecf90bd512d0b639", size = 392052 } wheels = [ - { url = "https://files.pythonhosted.org/packages/48/22/bc14c6f02e6dccaafb3eba95764c8f096714260c2aa5f76f654fd16a23dd/Mako-1.3.6-py3-none-any.whl", hash = "sha256:a91198468092a2f1a0de86ca92690fb0cfc43ca90ee17e15d93662b4c04b241a", size = 78557 }, + { url = "https://files.pythonhosted.org/packages/a7/d8/c516e830071b849ad60ee1026315bb8566381ac648ac8bda314569b4b7e2/Mako-1.3.7-py3-none-any.whl", hash = "sha256:d18f990ad57f800ce8e76cbfb0b74afe471c293517e9f5003ace6dad5aa72c36", size = 78949 }, ] [[package]] @@ -161,22 +163,25 @@ wheels = [ [[package]] name = "packaging" -version = "24.1" +version = "24.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/51/65/50db4dda066951078f0a96cf12f4b9ada6e4b811516bf0262c0f4f7064d4/packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002", size = 148788 } +sdist = { url = "https://files.pythonhosted.org/packages/d0/63/68dbb6eb2de9cb10ee4c9c14a0148804425e13c4fb20d61cce69f53106da/packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f", size = 163950 } wheels = [ - { url = "https://files.pythonhosted.org/packages/08/aa/cc0199a5f0ad350994d660967a8efb233fe0416e4639146c089643407ce6/packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124", size = 53985 }, + { url = "https://files.pythonhosted.org/packages/88/ef/eb23f262cca3c0c4eb7ab1933c3b1f03d021f2c48f54763065b6f0e321be/packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759", size = 65451 }, ] [[package]] name = "pdoc3" -version = "0.11.1" +version = "0.11.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "mako" }, { name = "markdown" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/40/a3/141d32ae6abfdd17f0314a3f6b93ce83becefc7f0f71f5902b6b8b993cd7/pdoc3-0.11.1.tar.gz", hash = "sha256:20bf2aad8110892573fd350b1bf95f6612b53b55e29b0fc143b5f2b5bcbabfb6", size = 97928 } +sdist = { url = "https://files.pythonhosted.org/packages/0a/11/5d7a91d51a6ad434e7fa15f7b331e446e0b2e77c31e60b60c153c4002722/pdoc3-0.11.3.tar.gz", hash = "sha256:e90ac2f0570b11e7a0e83ae21f353d70c1d33046bd6d98604deb535187cade15", size = 98948 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/20/b4/2e35d062116e77ecf406ae50c38049e9d28a81d28bc9838e38a7ad7789f8/pdoc3-0.11.3-py3-none-any.whl", hash = "sha256:db2d713ed2b034af38a0f5e0004c88259873df8eb8e950ca8513ef991408bcc5", size = 143654 }, +] [[package]] name = "pluggy" @@ -189,7 +194,7 @@ wheels = [ [[package]] name = "pytest" -version = "8.3.3" +version = "8.3.4" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, @@ -199,59 +204,101 @@ dependencies = [ { name = "pluggy" }, { name = "tomli", marker = "python_full_version < '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/8b/6c/62bbd536103af674e227c41a8f3dcd022d591f6eed5facb5a0f31ee33bbc/pytest-8.3.3.tar.gz", hash = "sha256:70b98107bd648308a7952b06e6ca9a50bc660be218d53c257cc1fc94fda10181", size = 1442487 } +sdist = { url = "https://files.pythonhosted.org/packages/05/35/30e0d83068951d90a01852cb1cef56e5d8a09d20c7f511634cc2f7e0372a/pytest-8.3.4.tar.gz", hash = "sha256:965370d062bce11e73868e0335abac31b4d3de0e82f4007408d242b4f8610761", size = 1445919 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/11/92/76a1c94d3afee238333bc0a42b82935dd8f9cf8ce9e336ff87ee14d9e1cf/pytest-8.3.4-py3-none-any.whl", hash = "sha256:50e16d954148559c9a74109af1eaf0c945ba2d8f30f0a3d3335edde19788b6f6", size = 343083 }, +] + +[[package]] +name = "pytest-asyncio" +version = "0.24.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pytest" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/52/6d/c6cf50ce320cf8611df7a1254d86233b3df7cc07f9b5f5cbcb82e08aa534/pytest_asyncio-0.24.0.tar.gz", hash = "sha256:d081d828e576d85f875399194281e92bf8a68d60d72d1a2faf2feddb6c46b276", size = 49855 } wheels = [ - { url = "https://files.pythonhosted.org/packages/6b/77/7440a06a8ead44c7757a64362dd22df5760f9b12dc5f11b6188cd2fc27a0/pytest-8.3.3-py3-none-any.whl", hash = "sha256:a6853c7375b2663155079443d2e45de913a911a11d669df02a50814944db57b2", size = 342341 }, + { url = "https://files.pythonhosted.org/packages/96/31/6607dab48616902f76885dfcf62c08d929796fc3b2d2318faf9fd54dbed9/pytest_asyncio-0.24.0-py3-none-any.whl", hash = "sha256:a811296ed596b69bf0b6f3dc40f83bcaf341b155a269052d82efa2b25ac7037b", size = 18024 }, ] [[package]] name = "ruff" -version = "0.7.1" +version = "0.8.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a6/21/5c6e05e0fd3fbb41be4fb92edbc9a04de70baf60adb61435ce0c6b8c3d55/ruff-0.7.1.tar.gz", hash = "sha256:9d8a41d4aa2dad1575adb98a82870cf5db5f76b2938cf2206c22c940034a36f4", size = 3181670 } +sdist = { url = "https://files.pythonhosted.org/packages/95/d0/8ff5b189d125f4260f2255d143bf2fa413b69c2610c405ace7a0a8ec81ec/ruff-0.8.1.tar.gz", hash = "sha256:3583db9a6450364ed5ca3f3b4225958b24f78178908d5c4bc0f46251ccca898f", size = 3313222 } wheels = [ - { url = "https://files.pythonhosted.org/packages/65/45/8a20a9920175c9c4892b2420f80ff3cf14949cf3067118e212f9acd9c908/ruff-0.7.1-py3-none-linux_armv6l.whl", hash = "sha256:cb1bc5ed9403daa7da05475d615739cc0212e861b7306f314379d958592aaa89", size = 10389268 }, - { url = "https://files.pythonhosted.org/packages/1b/d3/2f8382db2cf4f9488e938602e33e36287f9d26cb283aa31f11c31297ce79/ruff-0.7.1-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:27c1c52a8d199a257ff1e5582d078eab7145129aa02721815ca8fa4f9612dc35", size = 10188348 }, - { url = "https://files.pythonhosted.org/packages/a2/31/7d14e2a88da351200f844b7be889a0845d9e797162cf76b136d21b832a23/ruff-0.7.1-py3-none-macosx_11_0_arm64.whl", hash = "sha256:588a34e1ef2ea55b4ddfec26bbe76bc866e92523d8c6cdec5e8aceefeff02d99", size = 9841448 }, - { url = "https://files.pythonhosted.org/packages/db/99/738cafdc768eceeca0bd26c6f03e213aa91203d2278e1d95b1c31c4ece41/ruff-0.7.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94fc32f9cdf72dc75c451e5f072758b118ab8100727168a3df58502b43a599ca", size = 10674864 }, - { url = "https://files.pythonhosted.org/packages/fe/12/bcf2836b50eab53c65008383e7d55201e490d75167c474f14a16e1af47d2/ruff-0.7.1-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:985818742b833bffa543a84d1cc11b5e6871de1b4e0ac3060a59a2bae3969250", size = 10192105 }, - { url = "https://files.pythonhosted.org/packages/2b/71/261d5d668bf98b6c44e89bfb5dfa4cb8cb6c8b490a201a3d8030e136ea4f/ruff-0.7.1-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:32f1e8a192e261366c702c5fb2ece9f68d26625f198a25c408861c16dc2dea9c", size = 11194144 }, - { url = "https://files.pythonhosted.org/packages/90/1f/0926d18a3b566fa6e7b3b36093088e4ffef6b6ba4ea85a462d9a93f7e35c/ruff-0.7.1-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:699085bf05819588551b11751eff33e9ca58b1b86a6843e1b082a7de40da1565", size = 11917066 }, - { url = "https://files.pythonhosted.org/packages/cd/a8/9fac41f128b6a44ab4409c1493430b4ee4b11521e8aeeca19bfe1ce851f9/ruff-0.7.1-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:344cc2b0814047dc8c3a8ff2cd1f3d808bb23c6658db830d25147339d9bf9ea7", size = 11458821 }, - { url = "https://files.pythonhosted.org/packages/25/cd/59644168f086ab13fe4e02943b9489a0aa710171f66b178e179df5383554/ruff-0.7.1-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4316bbf69d5a859cc937890c7ac7a6551252b6a01b1d2c97e8fc96e45a7c8b4a", size = 12700379 }, - { url = "https://files.pythonhosted.org/packages/fb/30/3bac63619eb97174661829c07fc46b2055a053dee72da29d7c304c1cd2c0/ruff-0.7.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:79d3af9dca4c56043e738a4d6dd1e9444b6d6c10598ac52d146e331eb155a8ad", size = 11019813 }, - { url = "https://files.pythonhosted.org/packages/4b/af/f567b885b5cb3bcdbcca3458ebf210cc8c9c7a9f61c332d3c2a050c3b21e/ruff-0.7.1-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:c5c121b46abde94a505175524e51891f829414e093cd8326d6e741ecfc0a9112", size = 10662146 }, - { url = "https://files.pythonhosted.org/packages/bc/ad/eb930d3ad117a9f2f7261969c21559ebd82bb13b6e8001c7caed0d44be5f/ruff-0.7.1-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:8422104078324ea250886954e48f1373a8fe7de59283d747c3a7eca050b4e378", size = 10256911 }, - { url = "https://files.pythonhosted.org/packages/20/d5/af292ce70a016fcec792105ca67f768b403dd480a11888bc1f418fed0dd5/ruff-0.7.1-py3-none-musllinux_1_2_i686.whl", hash = "sha256:56aad830af8a9db644e80098fe4984a948e2b6fc2e73891538f43bbe478461b8", size = 10767488 }, - { url = "https://files.pythonhosted.org/packages/24/85/cc04a3bd027f433bebd2a097e63b3167653c079f7f13d8f9a1178e693412/ruff-0.7.1-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:658304f02f68d3a83c998ad8bf91f9b4f53e93e5412b8f2388359d55869727fd", size = 11093368 }, - { url = "https://files.pythonhosted.org/packages/0b/fb/c39cbf32d1f3e318674b8622f989417231794926b573f76dd4d0ca49f0f1/ruff-0.7.1-py3-none-win32.whl", hash = "sha256:b517a2011333eb7ce2d402652ecaa0ac1a30c114fbbd55c6b8ee466a7f600ee9", size = 8594180 }, - { url = "https://files.pythonhosted.org/packages/5a/71/ec8cdea34ecb90c830ca60d54ac7b509a7b5eab50fae27e001d4470fe813/ruff-0.7.1-py3-none-win_amd64.whl", hash = "sha256:f38c41fcde1728736b4eb2b18850f6d1e3eedd9678c914dede554a70d5241307", size = 9419751 }, - { url = "https://files.pythonhosted.org/packages/79/7b/884553415e9f0a9bf358ed52fb68b934e67ef6c5a62397ace924a1afdf9a/ruff-0.7.1-py3-none-win_arm64.whl", hash = "sha256:19aa200ec824c0f36d0c9114c8ec0087082021732979a359d6f3c390a6ff2a37", size = 8717402 }, + { url = "https://files.pythonhosted.org/packages/a2/d6/1a6314e568db88acdbb5121ed53e2c52cebf3720d3437a76f82f923bf171/ruff-0.8.1-py3-none-linux_armv6l.whl", hash = "sha256:fae0805bd514066f20309f6742f6ee7904a773eb9e6c17c45d6b1600ca65c9b5", size = 10532605 }, + { url = "https://files.pythonhosted.org/packages/89/a8/a957a8812e31facffb6a26a30be0b5b4af000a6e30c7d43a22a5232a3398/ruff-0.8.1-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b8a4f7385c2285c30f34b200ca5511fcc865f17578383db154e098150ce0a087", size = 10278243 }, + { url = "https://files.pythonhosted.org/packages/a8/23/9db40fa19c453fabf94f7a35c61c58f20e8200b4734a20839515a19da790/ruff-0.8.1-py3-none-macosx_11_0_arm64.whl", hash = "sha256:cd054486da0c53e41e0086e1730eb77d1f698154f910e0cd9e0d64274979a209", size = 9917739 }, + { url = "https://files.pythonhosted.org/packages/e2/a0/6ee2d949835d5701d832fc5acd05c0bfdad5e89cfdd074a171411f5ccad5/ruff-0.8.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2029b8c22da147c50ae577e621a5bfbc5d1fed75d86af53643d7a7aee1d23871", size = 10779153 }, + { url = "https://files.pythonhosted.org/packages/7a/25/9c11dca9404ef1eb24833f780146236131a3c7941de394bc356912ef1041/ruff-0.8.1-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2666520828dee7dfc7e47ee4ea0d928f40de72056d929a7c5292d95071d881d1", size = 10304387 }, + { url = "https://files.pythonhosted.org/packages/c8/b9/84c323780db1b06feae603a707d82dbbd85955c8c917738571c65d7d5aff/ruff-0.8.1-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:333c57013ef8c97a53892aa56042831c372e0bb1785ab7026187b7abd0135ad5", size = 11360351 }, + { url = "https://files.pythonhosted.org/packages/6b/e1/9d4bbb2ace7aad14ded20e4674a48cda5b902aed7a1b14e6b028067060c4/ruff-0.8.1-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:288326162804f34088ac007139488dcb43de590a5ccfec3166396530b58fb89d", size = 12022879 }, + { url = "https://files.pythonhosted.org/packages/75/28/752ff6120c0e7f9981bc4bc275d540c7f36db1379ba9db9142f69c88db21/ruff-0.8.1-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b12c39b9448632284561cbf4191aa1b005882acbc81900ffa9f9f471c8ff7e26", size = 11610354 }, + { url = "https://files.pythonhosted.org/packages/ba/8c/967b61c2cc8ebd1df877607fbe462bc1e1220b4a30ae3352648aec8c24bd/ruff-0.8.1-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:364e6674450cbac8e998f7b30639040c99d81dfb5bbc6dfad69bc7a8f916b3d1", size = 12813976 }, + { url = "https://files.pythonhosted.org/packages/7f/29/e059f945d6bd2d90213387b8c360187f2fefc989ddcee6bbf3c241329b92/ruff-0.8.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b22346f845fec132aa39cd29acb94451d030c10874408dbf776af3aaeb53284c", size = 11154564 }, + { url = "https://files.pythonhosted.org/packages/55/47/cbd05e5a62f3fb4c072bc65c1e8fd709924cad1c7ec60a1000d1e4ee8307/ruff-0.8.1-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:b2f2f7a7e7648a2bfe6ead4e0a16745db956da0e3a231ad443d2a66a105c04fa", size = 10760604 }, + { url = "https://files.pythonhosted.org/packages/bb/ee/4c3981c47147c72647a198a94202633130cfda0fc95cd863a553b6f65c6a/ruff-0.8.1-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:adf314fc458374c25c5c4a4a9270c3e8a6a807b1bec018cfa2813d6546215540", size = 10391071 }, + { url = "https://files.pythonhosted.org/packages/6b/e6/083eb61300214590b188616a8ac6ae1ef5730a0974240fb4bec9c17de78b/ruff-0.8.1-py3-none-musllinux_1_2_i686.whl", hash = "sha256:a885d68342a231b5ba4d30b8c6e1b1ee3a65cf37e3d29b3c74069cdf1ee1e3c9", size = 10896657 }, + { url = "https://files.pythonhosted.org/packages/77/bd/aacdb8285d10f1b943dbeb818968efca35459afc29f66ae3bd4596fbf954/ruff-0.8.1-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:d2c16e3508c8cc73e96aa5127d0df8913d2290098f776416a4b157657bee44c5", size = 11228362 }, + { url = "https://files.pythonhosted.org/packages/39/72/fcb7ad41947f38b4eaa702aca0a361af0e9c2bf671d7fd964480670c297e/ruff-0.8.1-py3-none-win32.whl", hash = "sha256:93335cd7c0eaedb44882d75a7acb7df4b77cd7cd0d2255c93b28791716e81790", size = 8803476 }, + { url = "https://files.pythonhosted.org/packages/e4/ea/cae9aeb0f4822c44651c8407baacdb2e5b4dcd7b31a84e1c5df33aa2cc20/ruff-0.8.1-py3-none-win_amd64.whl", hash = "sha256:2954cdbe8dfd8ab359d4a30cd971b589d335a44d444b6ca2cb3d1da21b75e4b6", size = 9614463 }, + { url = "https://files.pythonhosted.org/packages/eb/76/fbb4bd23dfb48fa7758d35b744413b650a9fd2ddd93bca77e30376864414/ruff-0.8.1-py3-none-win_arm64.whl", hash = "sha256:55873cc1a473e5ac129d15eccb3c008c096b94809d693fc7053f588b67822737", size = 8959621 }, ] [[package]] name = "setuptools" -version = "75.3.0" +version = "75.6.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ed/22/a438e0caa4576f8c383fa4d35f1cc01655a46c75be358960d815bfbb12bd/setuptools-75.3.0.tar.gz", hash = "sha256:fba5dd4d766e97be1b1681d98712680ae8f2f26d7881245f2ce9e40714f1a686", size = 1351577 } +sdist = { url = "https://files.pythonhosted.org/packages/43/54/292f26c208734e9a7f067aea4a7e282c080750c4546559b58e2e45413ca0/setuptools-75.6.0.tar.gz", hash = "sha256:8199222558df7c86216af4f84c30e9b34a61d8ba19366cc914424cdbd28252f6", size = 1337429 } wheels = [ - { url = "https://files.pythonhosted.org/packages/90/12/282ee9bce8b58130cb762fbc9beabd531549952cac11fc56add11dcb7ea0/setuptools-75.3.0-py3-none-any.whl", hash = "sha256:f2504966861356aa38616760c0f66568e535562374995367b4e69c7143cf6bcd", size = 1251070 }, + { url = "https://files.pythonhosted.org/packages/55/21/47d163f615df1d30c094f6c8bbb353619274edccf0327b185cc2493c2c33/setuptools-75.6.0-py3-none-any.whl", hash = "sha256:ce74b49e8f7110f9bf04883b730f4765b774ef3ef28f722cce7c273d253aaf7d", size = 1224032 }, ] [[package]] name = "tomli" -version = "2.0.2" +version = "2.2.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/35/b9/de2a5c0144d7d75a57ff355c0c24054f965b2dc3036456ae03a51ea6264b/tomli-2.0.2.tar.gz", hash = "sha256:d46d457a85337051c36524bc5349dd91b1877838e2979ac5ced3e710ed8a60ed", size = 16096 } +sdist = { url = "https://files.pythonhosted.org/packages/18/87/302344fed471e44a87289cf4967697d07e532f2421fdaf868a303cbae4ff/tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff", size = 17175 } wheels = [ - { url = "https://files.pythonhosted.org/packages/cf/db/ce8eda256fa131af12e0a76d481711abe4681b6923c27efb9a255c9e4594/tomli-2.0.2-py3-none-any.whl", hash = "sha256:2ebe24485c53d303f690b0ec092806a085f07af5a5aa1464f3931eec36caaa38", size = 13237 }, + { url = "https://files.pythonhosted.org/packages/43/ca/75707e6efa2b37c77dadb324ae7d9571cb424e61ea73fad7c56c2d14527f/tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249", size = 131077 }, + { url = "https://files.pythonhosted.org/packages/c7/16/51ae563a8615d472fdbffc43a3f3d46588c264ac4f024f63f01283becfbb/tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6", size = 123429 }, + { url = "https://files.pythonhosted.org/packages/f1/dd/4f6cd1e7b160041db83c694abc78e100473c15d54620083dbd5aae7b990e/tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a", size = 226067 }, + { url = "https://files.pythonhosted.org/packages/a9/6b/c54ede5dc70d648cc6361eaf429304b02f2871a345bbdd51e993d6cdf550/tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee", size = 236030 }, + { url = "https://files.pythonhosted.org/packages/1f/47/999514fa49cfaf7a92c805a86c3c43f4215621855d151b61c602abb38091/tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e", size = 240898 }, + { url = "https://files.pythonhosted.org/packages/73/41/0a01279a7ae09ee1573b423318e7934674ce06eb33f50936655071d81a24/tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4", size = 229894 }, + { url = "https://files.pythonhosted.org/packages/55/18/5d8bc5b0a0362311ce4d18830a5d28943667599a60d20118074ea1b01bb7/tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106", size = 245319 }, + { url = "https://files.pythonhosted.org/packages/92/a3/7ade0576d17f3cdf5ff44d61390d4b3febb8a9fc2b480c75c47ea048c646/tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8", size = 238273 }, + { url = "https://files.pythonhosted.org/packages/72/6f/fa64ef058ac1446a1e51110c375339b3ec6be245af9d14c87c4a6412dd32/tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff", size = 98310 }, + { url = "https://files.pythonhosted.org/packages/6a/1c/4a2dcde4a51b81be3530565e92eda625d94dafb46dbeb15069df4caffc34/tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b", size = 108309 }, + { url = "https://files.pythonhosted.org/packages/52/e1/f8af4c2fcde17500422858155aeb0d7e93477a0d59a98e56cbfe75070fd0/tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea", size = 132762 }, + { url = "https://files.pythonhosted.org/packages/03/b8/152c68bb84fc00396b83e7bbddd5ec0bd3dd409db4195e2a9b3e398ad2e3/tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8", size = 123453 }, + { url = "https://files.pythonhosted.org/packages/c8/d6/fc9267af9166f79ac528ff7e8c55c8181ded34eb4b0e93daa767b8841573/tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192", size = 233486 }, + { url = "https://files.pythonhosted.org/packages/5c/51/51c3f2884d7bab89af25f678447ea7d297b53b5a3b5730a7cb2ef6069f07/tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222", size = 242349 }, + { url = "https://files.pythonhosted.org/packages/ab/df/bfa89627d13a5cc22402e441e8a931ef2108403db390ff3345c05253935e/tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77", size = 252159 }, + { url = "https://files.pythonhosted.org/packages/9e/6e/fa2b916dced65763a5168c6ccb91066f7639bdc88b48adda990db10c8c0b/tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6", size = 237243 }, + { url = "https://files.pythonhosted.org/packages/b4/04/885d3b1f650e1153cbb93a6a9782c58a972b94ea4483ae4ac5cedd5e4a09/tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd", size = 259645 }, + { url = "https://files.pythonhosted.org/packages/9c/de/6b432d66e986e501586da298e28ebeefd3edc2c780f3ad73d22566034239/tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e", size = 244584 }, + { url = "https://files.pythonhosted.org/packages/1c/9a/47c0449b98e6e7d1be6cbac02f93dd79003234ddc4aaab6ba07a9a7482e2/tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98", size = 98875 }, + { url = "https://files.pythonhosted.org/packages/ef/60/9b9638f081c6f1261e2688bd487625cd1e660d0a85bd469e91d8db969734/tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4", size = 109418 }, + { url = "https://files.pythonhosted.org/packages/04/90/2ee5f2e0362cb8a0b6499dc44f4d7d48f8fff06d28ba46e6f1eaa61a1388/tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7", size = 132708 }, + { url = "https://files.pythonhosted.org/packages/c0/ec/46b4108816de6b385141f082ba99e315501ccd0a2ea23db4a100dd3990ea/tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c", size = 123582 }, + { url = "https://files.pythonhosted.org/packages/a0/bd/b470466d0137b37b68d24556c38a0cc819e8febe392d5b199dcd7f578365/tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13", size = 232543 }, + { url = "https://files.pythonhosted.org/packages/d9/e5/82e80ff3b751373f7cead2815bcbe2d51c895b3c990686741a8e56ec42ab/tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281", size = 241691 }, + { url = "https://files.pythonhosted.org/packages/05/7e/2a110bc2713557d6a1bfb06af23dd01e7dde52b6ee7dadc589868f9abfac/tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272", size = 251170 }, + { url = "https://files.pythonhosted.org/packages/64/7b/22d713946efe00e0adbcdfd6d1aa119ae03fd0b60ebed51ebb3fa9f5a2e5/tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140", size = 236530 }, + { url = "https://files.pythonhosted.org/packages/38/31/3a76f67da4b0cf37b742ca76beaf819dca0ebef26d78fc794a576e08accf/tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2", size = 258666 }, + { url = "https://files.pythonhosted.org/packages/07/10/5af1293da642aded87e8a988753945d0cf7e00a9452d3911dd3bb354c9e2/tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744", size = 243954 }, + { url = "https://files.pythonhosted.org/packages/5b/b9/1ed31d167be802da0fc95020d04cd27b7d7065cc6fbefdd2f9186f60d7bd/tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec", size = 98724 }, + { url = "https://files.pythonhosted.org/packages/c7/32/b0963458706accd9afcfeb867c0f9175a741bf7b19cd424230714d722198/tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69", size = 109383 }, + { url = "https://files.pythonhosted.org/packages/6e/c2/61d3e0f47e2b74ef40a68b9e6ad5984f6241a942f7cd3bbfbdbd03861ea9/tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc", size = 14257 }, ] [[package]] name = "zipp" -version = "3.20.2" +version = "3.21.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/54/bf/5c0000c44ebc80123ecbdddba1f5dcd94a5ada602a9c225d84b5aaa55e86/zipp-3.20.2.tar.gz", hash = "sha256:bc9eb26f4506fda01b81bcde0ca78103b6e62f991b381fec825435c836edbc29", size = 24199 } +sdist = { url = "https://files.pythonhosted.org/packages/3f/50/bad581df71744867e9468ebd0bcd6505de3b275e06f202c2cb016e3ff56f/zipp-3.21.0.tar.gz", hash = "sha256:2c9958f6430a2040341a52eb608ed6dd93ef4392e02ffe219417c1b28b5dd1f4", size = 24545 } wheels = [ - { url = "https://files.pythonhosted.org/packages/62/8b/5ba542fa83c90e09eac972fc9baca7a88e7e7ca4b221a89251954019308b/zipp-3.20.2-py3-none-any.whl", hash = "sha256:a817ac80d6cf4b23bf7f2828b7cabf326f15a001bea8b1f9b49631780ba28350", size = 9200 }, + { url = "https://files.pythonhosted.org/packages/b7/1a/7e4798e9339adc931158c9d69ecc34f5e6791489d469f5e50ec15e35f458/zipp-3.21.0-py3-none-any.whl", hash = "sha256:ac1bbe05fd2991f160ebce24ffbac5f6d11d83dc90891255885223d42b3cd931", size = 9630 }, ]