Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move validation routines to separate submodule #336

Merged
merged 3 commits into from
Sep 28, 2024
Merged

Move validation routines to separate submodule #336

merged 3 commits into from
Sep 28, 2024

Conversation

jan-janssen
Copy link
Member

@jan-janssen jan-janssen commented Sep 28, 2024

Summary by CodeRabbit

  • New Features

    • Introduced utility functions for validating and processing values, including checks for memory specifications and value ranges.
  • Bug Fixes

    • Improved validation of input values to ensure robustness and accuracy in handling memory specifications.
  • Refactor

    • Updated internal method calls to utilize new utility functions, streamlining the codebase and enhancing maintainability.
  • Tests

    • Refactored test cases to align with the updated validation functions, ensuring comprehensive coverage of new functionality.

Copy link

coderabbitai bot commented Sep 28, 2024

Walkthrough

The changes involve refactoring the BasisQueueAdapter class in pysqa/utils/basic.py by removing several internal utility methods and replacing them with direct calls to new functions in pysqa/utils/validate.py. This restructuring maintains the original functionality while enhancing the clarity and modularity of the code. Additionally, corresponding updates were made to the test files to reflect these changes, ensuring that tests now utilize the new utility functions instead of the removed methods.

Changes

Files Change Summary
pysqa/utils/basic.py Removed internal utility methods from BasisQueueAdapter and replaced them with calls to functions in pysqa/utils/validate.py.
pysqa/utils/validate.py Introduced new utility functions for value validation and memory specification processing, including value_error_if_none, value_in_range, _is_memory_string, and _memory_spec_string_to_value.
tests/test_basic.py Updated test cases to replace calls to removed methods with new utility functions, ensuring assertions remain consistent.
tests/test_sge.py Refactored tests to call the new value_in_range function directly instead of the private method from BasisQueueAdapter, updating assertions accordingly.

Sequence Diagram(s)

sequenceDiagram
    participant A as User
    participant B as BasisQueueAdapter
    participant C as ValidateFunctions

    A->>B: Call check_queue_parameters()
    B->>C: Call value_error_if_none(value)
    C-->>B: Return validation result
    B->>C: Call value_in_range(value, min, max)
    C-->>B: Return range check result
    B-->>A: Return final result
Loading

Poem

🐇 In the code where rabbits hop,
Functions flourish, never stop.
Old methods fade, new paths we trace,
Validation shines in its rightful place.
With every change, we dance and play,
Hopping forward, come what may! 🌟


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Outside diff range and nitpick comments (3)
tests/test_basic.py (2)

44-51: Consider adding explicit type checking.

These assertions test the function's behavior with memory strings and numeric strings. While the functionality seems correct, it might be beneficial to add explicit type checking in the value_in_range function to ensure it handles different input types consistently.


60-67: Consider adding edge case tests.

While these tests cover large values and upper limits, consider adding edge case tests, such as values exactly at the minimum and maximum limits, to ensure the function behaves correctly at the boundaries.

tests/test_sge.py (1)

50-66: LGTM: Test cases updated correctly.

The test_value_in_range method has been successfully updated to use the new value_in_range function from the pysqa.utils.validate module. This change is consistent with the refactoring objectives and maintains the same test coverage as before.

Consider parameterizing these tests for improved readability and maintainability. Here's an example of how you could refactor this:

import pytest

@pytest.mark.parametrize("value, value_min, value_max, expected", [
    (None, None, None, None),
    (None, 1, None, 1),
    (None, None, 1, 1),
    (1, None, None, 1),
    (0, 1, None, 1),
    (2, None, 1, 1),
    (1, 0, 2, 1),
])
def test_value_in_range(value, value_min, value_max, expected):
    assert value_in_range(value, value_min, value_max) == expected

This approach would make it easier to add new test cases in the future and improve the overall readability of the tests.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 3e29a93 and 947f53c.

📒 Files selected for processing (4)
  • pysqa/utils/basic.py (3 hunks)
  • pysqa/utils/validate.py (1 hunks)
  • tests/test_basic.py (2 hunks)
  • tests/test_sge.py (2 hunks)
🔇 Additional comments (11)
tests/test_basic.py (5)

9-9: LGTM: Import statement is correct and consistent with the refactoring.

The new import statement correctly imports the value_in_range function from the pysqa.utils.validate module, which aligns with the refactoring described in the PR objectives.


40-43: LGTM: Basic integer and string input tests look good.

These assertions correctly test the value_in_range function with both integer and string inputs, verifying its behavior with different minimum values.


52-59: LGTM: Tests for values within range are correct.

These assertions verify that the function correctly handles values within the specified range, both for memory strings and integers.


68-71: LGTM: Lower limit test is correct.

This assertion correctly tests the function's behavior when the input is below the specified minimum value.


40-71: Overall, the changes look good and align with the PR objectives.

The refactoring has been done consistently, replacing all instances of BasisQueueAdapter._value_in_range with the new value_in_range function. The test cases cover a wide range of scenarios, ensuring that the functionality remains intact after the refactoring.

Some minor suggestions for improvement:

  1. Consider adding explicit type checking in the value_in_range function to handle different input types consistently.
  2. Add edge case tests for values exactly at the minimum and maximum limits.

These additions would further strengthen the test suite and ensure robust behavior of the value_in_range function across all possible inputs.

To ensure that all instances of _value_in_range have been replaced, run the following script:

tests/test_sge.py (2)

9-9: LGTM: Import statement added correctly.

The new import statement for value_in_range from pysqa.utils.validate is consistent with the refactoring mentioned in the PR objectives. This change improves the modularity of the code by separating validation functions into their own submodule.


Line range hint 1-166: Summary: Changes align with PR objectives and maintain test integrity.

The modifications in this file successfully implement the PR objective of moving validation routines to a separate submodule. The value_in_range function is now imported from pysqa.utils.validate, and the test_value_in_range method has been updated accordingly. These changes improve the modularity of the code while maintaining the same test coverage.

The rest of the file remains unchanged, indicating that the refactoring has been done with minimal impact on other parts of the test suite. This is a positive sign, suggesting that the changes are well-contained and focused.

To ensure that no other parts of the file are affected by this change, please run the following command:

If this command returns no results, it confirms that all instances of the old method have been replaced.

✅ Verification successful

Correction Needed: Invalid file type in ripgrep command

Apologies for the oversight. The correct file type for Python in ripgrep is py. Please run the following command to check for any remaining references to _value_in_range in tests/test_sge.py:


Further Verification Needed: Ensure _value_in_range is Fully Removed

While the test method test_value_in_range is correctly present, we need to confirm that there are no lingering references to _value_in_range elsewhere in tests/test_sge.py. Please run the following command to check for any remaining usages of _value_in_range outside the test method definition:


Verification Successful: No remaining references to _value_in_range found

The search results indicate that there are no remaining references to _value_in_range outside the test_value_in_range method in tests/test_sge.py. This confirms that the value_in_range function has been successfully moved to the separate submodule with no lingering dependencies in this test file.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for any remaining references to _value_in_range in the file

rg --type python '_value_in_range' tests/test_sge.py

Length of output: 83


Script:

#!/bin/bash
# Revised command to check for any remaining references to _value_in_range in tests/test_sge.py

rg --type py '_value_in_range' tests/test_sge.py

Length of output: 83


Script:

#!/bin/bash
# Search for usages of _value_in_range excluding its definition in test_value_in_range

rg --type py -e '_value_in_range\b' tests/test_sge.py | grep -v 'def test_value_in_range'

Length of output: 91

pysqa/utils/basic.py (4)

15-15: LGTM: Import statement added for validation functions

The addition of this import statement is consistent with the refactoring to move validation routines to a separate submodule. This change improves code organization and modularity.


375-384: LGTM: Validation logic updated to use imported functions

The check_queue_parameters method has been updated to use the imported value_in_range function instead of the internal _value_in_range method. This change is consistent with the refactoring goal and maintains the original functionality while improving code modularity.


468-468: LGTM: Command validation updated to use imported function

The _job_submission_template method now uses the imported value_error_if_none function to validate the command parameter. This change is in line with the refactoring goal and maintains the original functionality while improving code organization.


Line range hint 1-568: Overall: Successful refactoring of validation routines

The changes in this file successfully implement the goal of moving validation routines to a separate submodule. The refactoring:

  1. Removes internal utility methods and replaces them with imported functions.
  2. Maintains the original functionality while improving code organization and modularity.
  3. Adheres to the principle of separation of concerns.

These changes will make the code easier to maintain and test in the future. Great job on this refactoring effort!

Comment on lines +5 to +20
def value_error_if_none(value: str) -> None:
"""
Raise a ValueError if the value is None or not a string.

Args:
value (str/None): The value to check.

Raises:
ValueError: If the value is None.
TypeError: If the value is not a string.
"""
if value is None:
raise ValueError("Value cannot be None.")
if not isinstance(value, str):
raise TypeError()

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Consider renaming the function and adding informative error messages

The function value_error_if_none checks if value is None or not a string, but the name suggests it only raises a ValueError if value is None. Consider renaming the function to better reflect its functionality, such as validate_string_value.

Additionally, the TypeError raised at line 19 lacks an informative message. Including an error message will aid in debugging.

Apply this diff to address the issues:

-def value_error_if_none(value: str) -> None:
+def validate_string_value(value: str) -> None:
     """
-    Raise a ValueError if the value is None or not a string.
+    Validate that the value is a non-None string.

     Args:
-        value (str/None): The value to check.
+        value (str): The value to check.

     Raises:
         ValueError: If the value is None.
         TypeError: If the value is not a string.
     """
     if value is None:
         raise ValueError("Value cannot be None.")
     if not isinstance(value, str):
-        raise TypeError()
+        raise TypeError("Value must be a string.")
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def value_error_if_none(value: str) -> None:
"""
Raise a ValueError if the value is None or not a string.
Args:
value (str/None): The value to check.
Raises:
ValueError: If the value is None.
TypeError: If the value is not a string.
"""
if value is None:
raise ValueError("Value cannot be None.")
if not isinstance(value, str):
raise TypeError()
def validate_string_value(value: str) -> None:
"""
Validate that the value is a non-None string.
Args:
value (str): The value to check.
Raises:
ValueError: If the value is None.
TypeError: If the value is not a string.
"""
if value is None:
raise ValueError("Value cannot be None.")
if not isinstance(value, str):
raise TypeError("Value must be a string.")

Comment on lines +65 to +77
def _is_memory_string(value: str) -> bool:
"""
Check if a string specifies a certain amount of memory.

Args:
value (str): The string to check.

Returns:
bool: True if the string matches a memory specification, False otherwise.
"""
memory_spec_pattern = r"[0-9]+[bBkKmMgGtT]?"
return re.findall(memory_spec_pattern, value)[0] == value

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Prevent IndexError in _is_memory_string when input doesn't match the pattern

In the function _is_memory_string, if the input value does not match the memory_spec_pattern, re.findall may return an empty list, leading to an IndexError when accessing [0]. To avoid this, consider using re.fullmatch to check if the entire string matches the pattern.

Apply this change to fix the issue:

 def _is_memory_string(value: str) -> bool:
     """
     Check if a string specifies a certain amount of memory.
     """
     memory_spec_pattern = r"[0-9]+[bBkKmMgGtT]?"
-    return re.findall(memory_spec_pattern, value)[0] == value
+    return bool(re.fullmatch(memory_spec_pattern, value))
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def _is_memory_string(value: str) -> bool:
"""
Check if a string specifies a certain amount of memory.
Args:
value (str): The string to check.
Returns:
bool: True if the string matches a memory specification, False otherwise.
"""
memory_spec_pattern = r"[0-9]+[bBkKmMgGtT]?"
return re.findall(memory_spec_pattern, value)[0] == value
def _is_memory_string(value: str) -> bool:
"""
Check if a string specifies a certain amount of memory.
Args:
value (str): The string to check.
Returns:
bool: True if the string matches a memory specification, False otherwise.
"""
memory_spec_pattern = r"[0-9]+[bBkKmMgGtT]?"
return bool(re.fullmatch(memory_spec_pattern, value))

Comment on lines +22 to +63
def value_in_range(
value: Union[int, float, None],
value_min: Union[int, float, None] = None,
value_max: Union[int, float, None] = None,
) -> Union[int, float, None]:
"""
Check if a value is within a specified range.

Args:
value (int/float/None): The value to check.
value_min (int/float/None): The minimum value. Defaults to None.
value_max (int/float/None): The maximum value. Defaults to None.

Returns:
int/float/None: The value if it is within the range, otherwise the minimum or maximum value.
"""

if value is not None:
value_, value_min_, value_max_ = [
(
_memory_spec_string_to_value(v)
if v is not None and isinstance(v, str)
else v
)
for v in (value, value_min, value_max)
]
# ATTENTION: '60000' is interpreted as '60000M' since default magnitude is 'M'
# ATTENTION: int('60000') is interpreted as '60000B' since _memory_spec_string_to_value return the size in
# ATTENTION: bytes, as target_magnitude = 'b'
# We want to compare the the actual (k,m,g)byte value if there is any
if value_min_ is not None and value_ < value_min_:
return value_min
if value_max_ is not None and value_ > value_max_:
return value_max
return value
else:
if value_min is not None:
return value_min
if value_max is not None:
return value_max
return value

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Update type annotations and documentation to include string inputs

The function value_in_range handles value, value_min, and value_max, which can be string representations of memory sizes (e.g., "500M"). However, the type annotations and docstrings specify these parameters as Union[int, float, None]. To ensure clarity and type correctness, consider updating the type annotations and documentation to reflect that these parameters can also be strings.

Apply this diff to update the type annotations and documentation:

 def value_in_range(
-    value: Union[int, float, None],
-    value_min: Union[int, float, None] = None,
-    value_max: Union[int, float, None] = None,
-) -> Union[int, float, None]:
+    value: Union[int, float, str, None],
+    value_min: Union[int, float, str, None] = None,
+    value_max: Union[int, float, str, None] = None,
+) -> Union[int, float, str, None]:
     """
     Check if a value is within a specified range.

     Args:
-        value (int/float/None): The value to check.
-        value_min (int/float/None): The minimum value. Defaults to None.
-        value_max (int/float/None): The maximum value. Defaults to None.
+        value (int/float/str/None): The value to check.
+        value_min (int/float/str/None): The minimum value. Defaults to None.
+        value_max (int/float/str/None): The maximum value. Defaults to None.

     Returns:
-        int/float/None: The value if it is within the range, otherwise the minimum or maximum value.
+        int/float/str/None: The value if it is within the range, otherwise the minimum or maximum value.
     """
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def value_in_range(
value: Union[int, float, None],
value_min: Union[int, float, None] = None,
value_max: Union[int, float, None] = None,
) -> Union[int, float, None]:
"""
Check if a value is within a specified range.
Args:
value (int/float/None): The value to check.
value_min (int/float/None): The minimum value. Defaults to None.
value_max (int/float/None): The maximum value. Defaults to None.
Returns:
int/float/None: The value if it is within the range, otherwise the minimum or maximum value.
"""
if value is not None:
value_, value_min_, value_max_ = [
(
_memory_spec_string_to_value(v)
if v is not None and isinstance(v, str)
else v
)
for v in (value, value_min, value_max)
]
# ATTENTION: '60000' is interpreted as '60000M' since default magnitude is 'M'
# ATTENTION: int('60000') is interpreted as '60000B' since _memory_spec_string_to_value return the size in
# ATTENTION: bytes, as target_magnitude = 'b'
# We want to compare the the actual (k,m,g)byte value if there is any
if value_min_ is not None and value_ < value_min_:
return value_min
if value_max_ is not None and value_ > value_max_:
return value_max
return value
else:
if value_min is not None:
return value_min
if value_max is not None:
return value_max
return value
def value_in_range(
value: Union[int, float, str, None],
value_min: Union[int, float, str, None] = None,
value_max: Union[int, float, str, None] = None,
) -> Union[int, float, str, None]:
"""
Check if a value is within a specified range.
Args:
value (int/float/str/None): The value to check.
value_min (int/float/str/None): The minimum value. Defaults to None.
value_max (int/float/str/None): The maximum value. Defaults to None.
Returns:
int/float/str/None: The value if it is within the range, otherwise the minimum or maximum value.
"""
if value is not None:
value_, value_min_, value_max_ = [
(
_memory_spec_string_to_value(v)
if v is not None and isinstance(v, str)
else v
)
for v in (value, value_min, value_max)
]
# ATTENTION: '60000' is interpreted as '60000M' since default magnitude is 'M'
# ATTENTION: int('60000') is interpreted as '60000B' since _memory_spec_string_to_value return the size in
# ATTENTION: bytes, as target_magnitude = 'b'
# We want to compare the the actual (k,m,g)byte value if there is any
if value_min_ is not None and value_ < value_min_:
return value_min
if value_max_ is not None and value_ > value_max_:
return value_max
return value
else:
if value_min is not None:
return value_min
if value_max is not None:
return value_max
return value

Comment on lines +79 to +111
def _memory_spec_string_to_value(
value: str, default_magnitude: str = "m", target_magnitude: str = "b"
) -> Union[int, float]:
"""
Converts a valid memory string (tested by _is_memory_string) into an integer/float value of desired
magnitude `default_magnitude`. If it is a plain integer string (e.g.: '50000') it will be interpreted with
the magnitude passed in by the `default_magnitude`. The output will rescaled to `target_magnitude`

Args:
value (str): The string to convert.
default_magnitude (str): The magnitude for interpreting plain integer strings [b, B, k, K, m, M, g, G, t, T]. Defaults to "m".
target_magnitude (str): The magnitude to which the output value should be converted [b, B, k, K, m, M, g, G, t, T]. Defaults to "b".

Returns:
Union[int, float]: The value of the string in `target_magnitude` units.
"""
magnitude_mapping = {"b": 0, "k": 1, "m": 2, "g": 3, "t": 4}
if _is_memory_string(value):
integer_pattern = r"[0-9]+"
magnitude_pattern = r"[bBkKmMgGtT]+"
integer_value = int(re.findall(integer_pattern, value)[0])

magnitude = re.findall(magnitude_pattern, value)
if len(magnitude) > 0:
magnitude = magnitude[0].lower()
else:
magnitude = default_magnitude.lower()
# Convert it to default magnitude = megabytes
return (integer_value * 1024 ** magnitude_mapping[magnitude]) / (
1024 ** magnitude_mapping[target_magnitude]
)
else:
return value
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Ensure consistent return types and handle invalid inputs in _memory_spec_string_to_value

The function _memory_spec_string_to_value is declared to return Union[int, float], but if the input value is not a valid memory string, it returns value unmodified, which could be a str. This inconsistency may lead to type errors downstream. Instead, consider raising a ValueError when the input is invalid to maintain consistent return types.

Also, when extracting integer_value and magnitude, there is a potential for IndexError if re.findall returns an empty list. Adding checks to ensure matches are found before accessing them will prevent this error.

Apply these changes to address the issues:

 def _memory_spec_string_to_value(
     value: str, default_magnitude: str = "m", target_magnitude: str = "b"
 ) -> Union[int, float]:
     """
     Converts a valid memory string into an integer/float value.
     """
     magnitude_mapping = {"b": 0, "k": 1, "m": 2, "g": 3, "t": 4}
     if _is_memory_string(value):
         integer_pattern = r"[0-9]+"
         magnitude_pattern = r"[bBkKmMgGtT]+"
-        integer_value = int(re.findall(integer_pattern, value)[0])
+        integer_match = re.findall(integer_pattern, value)
+        if not integer_match:
+            raise ValueError(f"Invalid integer value in memory specification: {value}")
+        integer_value = int(integer_match[0])

-        magnitude = re.findall(magnitude_pattern, value)
-        if len(magnitude) > 0:
-            magnitude = magnitude[0].lower()
+        magnitude_match = re.findall(magnitude_pattern, value)
+        if magnitude_match:
+            magnitude = magnitude_match[0].lower()
+        else:
+            magnitude = default_magnitude.lower()
         return (integer_value * 1024 ** magnitude_mapping[magnitude]) / (
             1024 ** magnitude_mapping[target_magnitude]
         )
     else:
-        return value
+        raise ValueError(f"Invalid memory specification: {value}")
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def _memory_spec_string_to_value(
value: str, default_magnitude: str = "m", target_magnitude: str = "b"
) -> Union[int, float]:
"""
Converts a valid memory string (tested by _is_memory_string) into an integer/float value of desired
magnitude `default_magnitude`. If it is a plain integer string (e.g.: '50000') it will be interpreted with
the magnitude passed in by the `default_magnitude`. The output will rescaled to `target_magnitude`
Args:
value (str): The string to convert.
default_magnitude (str): The magnitude for interpreting plain integer strings [b, B, k, K, m, M, g, G, t, T]. Defaults to "m".
target_magnitude (str): The magnitude to which the output value should be converted [b, B, k, K, m, M, g, G, t, T]. Defaults to "b".
Returns:
Union[int, float]: The value of the string in `target_magnitude` units.
"""
magnitude_mapping = {"b": 0, "k": 1, "m": 2, "g": 3, "t": 4}
if _is_memory_string(value):
integer_pattern = r"[0-9]+"
magnitude_pattern = r"[bBkKmMgGtT]+"
integer_value = int(re.findall(integer_pattern, value)[0])
magnitude = re.findall(magnitude_pattern, value)
if len(magnitude) > 0:
magnitude = magnitude[0].lower()
else:
magnitude = default_magnitude.lower()
# Convert it to default magnitude = megabytes
return (integer_value * 1024 ** magnitude_mapping[magnitude]) / (
1024 ** magnitude_mapping[target_magnitude]
)
else:
return value
def _memory_spec_string_to_value(
value: str, default_magnitude: str = "m", target_magnitude: str = "b"
) -> Union[int, float]:
"""
Converts a valid memory string (tested by _is_memory_string) into an integer/float value of desired
magnitude `default_magnitude`. If it is a plain integer string (e.g.: '50000') it will be interpreted with
the magnitude passed in by the `default_magnitude`. The output will rescaled to `target_magnitude`
Args:
value (str): The string to convert.
default_magnitude (str): The magnitude for interpreting plain integer strings [b, B, k, K, m, M, g, G, t, T]. Defaults to "m".
target_magnitude (str): The magnitude to which the output value should be converted [b, B, k, K, m, M, g, G, t, T]. Defaults to "b".
Returns:
Union[int, float]: The value of the string in `target_magnitude` units.
"""
magnitude_mapping = {"b": 0, "k": 1, "m": 2, "g": 3, "t": 4}
if _is_memory_string(value):
integer_pattern = r"[0-9]+"
magnitude_pattern = r"[bBkKmMgGtT]+"
integer_match = re.findall(integer_pattern, value)
if not integer_match:
raise ValueError(f"Invalid integer value in memory specification: {value}")
integer_value = int(integer_match[0])
magnitude_match = re.findall(magnitude_pattern, value)
if magnitude_match:
magnitude = magnitude_match[0].lower()
else:
magnitude = default_magnitude.lower()
# Convert it to default magnitude = megabytes
return (integer_value * 1024 ** magnitude_mapping[magnitude]) / (
1024 ** magnitude_mapping[target_magnitude]
)
else:
raise ValueError(f"Invalid memory specification: {value}")

@jan-janssen jan-janssen merged commit 8dc16e7 into main Sep 28, 2024
17 of 18 checks passed
@jan-janssen jan-janssen deleted the validate branch September 28, 2024 09:04
@coderabbitai coderabbitai bot mentioned this pull request Sep 28, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant