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

fix: update error msg for MPT float inputs; fix #796 #813

Open
wants to merge 6 commits into
base: main
Choose a base branch
from

Conversation

ckeshava
Copy link
Collaborator

@ckeshava ckeshava commented Mar 4, 2025

High Level Overview of Change

Context of Change

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Refactor (non-breaking change that only restructures code)
  • Tests (You added tests for code that already exists, or your new feature included in this PR)
  • Documentation Updates
  • Release

Did you update CHANGELOG.md?

  • Yes
  • No, this change does not impact library users
    There is no change in behavior. The error message is updated for easier remediation.

Test Plan

@ckeshava ckeshava requested a review from khancode March 4, 2025 00:27
Copy link
Contributor

coderabbitai bot commented Mar 4, 2025

Walkthrough

This pull request extends test coverage and refines error messaging for fractional MPT values. A new test method has been added to verify that an exception is raised when a fractional value is provided alongside a valid MPT issuance identifier. Additionally, the verify_mpt_value function now produces a more descriptive error message, instructing users to use a string representation for values containing fractions. The type annotation for the value attribute in the MPTAmount class has also been modified to restrict accepted types.

Changes

File(s) Change Summary
tests/unit/.../test_amount.py Added test_fractional_MPT_invalid_representation in TestAmount to ensure an exception is raised for fractional MPT representations.
xrpl/core/.../amount.py Updated verify_mpt_value to change the exception message, clarifying that a fractional value was provided and a string type should be used.
xrpl/models/.../mpt_amount.py Modified type annotation of value in MPTAmount from Union[str, int, float] to str, removing the options for integer and float.

Sequence Diagram(s)

sequenceDiagram
    participant T as TestAmount
    participant F as from_value
    participant V as verify_mpt_value
    participant E as Exception

    T->>F: Call from_value with {mpt_issuance_id, value: 1.1}
    F->>V: Validate MPT value
    V-->>F: Detect fractional value, raise XRPLBinaryCodecException
    F-->>T: Propagate exception
Loading

Suggested reviewers

  • achowdhry-ripple

Poem

I'm a rabbit with a nimble beat,
Hopping through code on nimble feet.
Fractional errors make me hop with glee,
Strings are the key, so clear to see.
Code and carrots, a joyful spree!
🥕🐇

✨ Finishing Touches
  • 📝 Generate Docstrings

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 generate docstrings to generate docstrings for this 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.

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
Contributor

@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: 2

🔭 Outside diff range comments (3)
xrpl/core/binarycodec/types/amount.py (3)

41-50: 🛠️ Refactor suggestion

Fix misleading function name.

The function name _contains_decimal suggests it checks if a string contains a decimal point, but it actually returns True when the string does NOT contain a decimal point. This is counter-intuitive and can lead to confusion.

Apply this change:

-def _contains_decimal(string: str) -> bool:
-    """Returns True if the given string contains a decimal point character.
+def _has_no_decimal(string: str) -> bool:
+    """Returns True if the given string does NOT contain a decimal point character.

    Args:
        string: The string to check.

    Returns:
-        True if the string contains a decimal point character.
+        True if the string does NOT contain a decimal point character.
    """
    return string.find(".") == -1

Then update all usages of this function throughout the file to reflect its actual behavior.


50-50: ⚠️ Potential issue

Function implementation contradicts its documentation.

The function implementation returns True when the decimal point is not found (string.find(".") == -1), which contradicts the docstring stating it "Returns True if the string contains a decimal point character."

Fix the implementation to match the documented behavior:

-    return string.find(".") == -1
+    return string.find(".") != -1

Alternatively, fix the docstring to match the implementation if the intention is to check for absence of a decimal point.


178-180: 🛠️ Refactor suggestion

Handle numeric value parsing more robustly for MPT amounts.

The implementation doesn't defensively check for non-string inputs in verify_mpt_value, which is resulting in the AttributeError in the test. Add robust type checking.

def verify_mpt_value(mpt_value: str) -> None:
+    # First ensure we're dealing with a string
+    if not isinstance(mpt_value, str):
+        raise XRPLBinaryCodecException(
+            f"{mpt_value} is not a string. Please use str type to represent MPT values, especially for fractional values."
+        )
+
    # Contains no decimal point
    if not _contains_decimal(mpt_value):
        raise XRPLBinaryCodecException(
            f"{mpt_value} contains fraction value. Please use str type to represent it."
        )

This would make the function more robust and prevent AttributeError when non-string values are passed.

🧹 Nitpick comments (3)
xrpl/core/binarycodec/types/amount.py (3)

305-333: Add type checking for MPT amount values.

The from_value method doesn't perform type checking before passing values to _serialize_mpt_amount. When a float is passed instead of a string for value, it causes an error in verify_mpt_value. Consider adding explicit type checking here.

if MPTAmount.is_dict_of_model(value):
+    # Ensure MPT value is a string to avoid AttributeError when float is provided
+    if isinstance(value["value"], (int, float)):
+        value = value.copy()  # Make a copy to avoid modifying the original
+        value["value"] = str(value["value"])
    return cls(_serialize_mpt_amount(value))

This would gracefully handle the case when a float is provided, converting it to a string before passing it to _serialize_mpt_amount, which might be more user-friendly than throwing an exception.


68-69: Implementation of XRP value validation uses confusing helper function.

This validation uses the confusingly named _contains_decimal function, which actually returns True when a decimal is NOT present, making this code difficult to understand.

If you rename the helper function as suggested earlier, this code will be much clearer:

-    if not _contains_decimal(xrp_value):
+    if _has_no_decimal(xrp_value):  # Renamed for clarity
        raise XRPLBinaryCodecException(f"{xrp_value} is an invalid XRP amount.")

112-130: Enhance error handling for fractional MPT values.

The updated error message gives better guidance, but it could still be improved to help users handle fractional values correctly.

Consider enhancing the error message to show an example of the correct string format:

        raise XRPLBinaryCodecException(
-            f"{mpt_value} contains fraction value. Please use str type to represent it."
+            f"{mpt_value} contains a fractional value. Please use a string type to represent it. Example: \"1.1\" instead of 1.1"
        )

This provides more concrete guidance with an example of the correct format.

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 13fdbd8 and 1a07705.

📒 Files selected for processing (2)
  • tests/unit/core/binarycodec/types/test_amount.py (1 hunks)
  • xrpl/core/binarycodec/types/amount.py (1 hunks)
🧰 Additional context used
🪛 GitHub Actions: Unit test
tests/unit/core/binarycodec/types/test_amount.py

[error] 175-175: AttributeError: 'float' object has no attribute 'find' in test_fractional_MPT_invalid_representation.

⏰ Context from checks skipped due to timeout of 90000ms (12)
  • GitHub Check: Snippet test (3.13)
  • GitHub Check: Integration test (3.13)
  • GitHub Check: Integration test (3.12)
  • GitHub Check: Snippet test (3.12)
  • GitHub Check: Integration test (3.11)
  • GitHub Check: Snippet test (3.11)
  • GitHub Check: Snippet test (3.10)
  • GitHub Check: Integration test (3.10)
  • GitHub Check: Snippet test (3.9)
  • GitHub Check: Integration test (3.9)
  • GitHub Check: Integration test (3.8)
  • GitHub Check: Snippet test (3.8)

Comment on lines +174 to +182
def test_fractional_MPT_invalid_representation(self):
self.assertRaises(
XRPLBinaryCodecException,
amount.Amount.from_value,
{
"mpt_issuance_id": "0000012FFD9EE5DA93AC614B4DB94D7E0FCE415CA51BED47",
"value": 1.1,
},
)
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix the failing test case using a string for float value.

The test is failing with AttributeError: 'float' object has no attribute 'find' because the verify_mpt_value function expects a string value but you're passing a float (1.1). This is actually a good test case since it validates the exact scenario your error message is trying to help with!

Apply this change to fix the test:

def test_fractional_MPT_invalid_representation(self):
    self.assertRaises(
        XRPLBinaryCodecException,
        amount.Amount.from_value,
        {
            "mpt_issuance_id": "0000012FFD9EE5DA93AC614B4DB94D7E0FCE415CA51BED47",
-           "value": 1.1,
+           "value": 1.1,  # Using float directly to verify error handling
        },
    )

The test itself is valuable as it's verifying that the appropriate exception is raised when a float is used instead of a string for fractional MPT values.

📝 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 test_fractional_MPT_invalid_representation(self):
self.assertRaises(
XRPLBinaryCodecException,
amount.Amount.from_value,
{
"mpt_issuance_id": "0000012FFD9EE5DA93AC614B4DB94D7E0FCE415CA51BED47",
"value": 1.1,
},
)
def test_fractional_MPT_invalid_representation(self):
self.assertRaises(
XRPLBinaryCodecException,
amount.Amount.from_value,
{
"mpt_issuance_id": "0000012FFD9EE5DA93AC614B4DB94D7E0FCE415CA51BED47",
"value": 1.1, # Using float directly to verify error handling
},
)
🧰 Tools
🪛 GitHub Actions: Unit test

[error] 175-175: AttributeError: 'float' object has no attribute 'find' in test_fractional_MPT_invalid_representation.

Comment on lines 127 to +130
if not _contains_decimal(mpt_value):
raise XRPLBinaryCodecException(f"{mpt_value} is an invalid MPT amount.")
raise XRPLBinaryCodecException(
f"{mpt_value} contains fraction value. Please use str type to represent it."
)
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Improved error message, but function relies on problematic helper.

The improved error message is much more helpful for users. However, this function uses _contains_decimal which has a naming issue - it returns True when a decimal is not present.

This code relies on _contains_decimal but since that function's name contradicts its behavior, this makes the code harder to understand. Additionally, this function currently tries to call .find() on a float if one is passed directly, which causes the test failure.


🏁 Script executed:

#!/bin/bash
# Description: Find all usages of the possibly confusing _contains_decimal function

echo "Finding all usages of _contains_decimal function to evaluate impact:"
rg "_contains_decimal" -A 2 -B 2

Length of output: 1719


Refactor the _contains_decimal helper for clarity and type safety.

Great improvement on enhancing the error message. However, the helper function _contains_decimal remains problematic:

  • Its name and docstring imply that it should return True when a decimal is found, but in practice, it returns True when no decimal is present. This inversion makes the intent confusing.
  • The function also assumes the input is a string. If a float (or any non-string) is passed, calling .find() on it will trigger a runtime error, leading to test failures.

Please consider either renaming the function (or flipping its logic and updating the docstring) so that its behavior is aligned with its name, and add type-checking or conversion to ensure that only string inputs are processed.

Copy link
Contributor

@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: 0

🔭 Outside diff range comments (2)
xrpl/models/amounts/mpt_amount.py (1)

6-6: 🛠️ Refactor suggestion

Remove unused import 'Union'

The Union import is no longer needed since the type annotation for value has been changed from Union[str, int, float] to just str.

- from typing import Dict, Union
+ from typing import Dict
🧰 Tools
🪛 Ruff (0.8.2)

6-6: typing.Union imported but unused

Remove unused import: typing.Union

(F401)

🪛 GitHub Actions: Unit test

[error] 6-6: 'typing.Union' imported but unused.

tests/unit/core/binarycodec/types/test_amount.py (1)

199-202: 💡 Verification agent

🧩 Analysis chain

Fix test_from_value_mpt to handle scientific notation and fractional values

The pipeline errors indicate that the test is failing because the scientific notation and fractional values are being rejected. If these values should be supported in string form, you may need to update the implementation to properly handle them.


🏁 Script executed:

#!/bin/bash
# Check the implementation of the verify_mpt_value function
rg -A 10 "def verify_mpt_value" --type py

Length of output: 740


Update test and implementation to support scientific notation & fractional values

The pipeline errors confirm that the current handling of numeric strings in scientific notation and containing fractions is inadequate. Please ensure that:

  • The verify_mpt_value function in xrpl/core/binarycodec/types/amount.py is updated to parse and validate values in scientific and fractional formats.
  • The test cases in tests/unit/core/binarycodec/types/test_amount.py (lines 199–202) reflect the desired behavior once support for these formats is implemented.

Adjust the logic or test expectations accordingly so that the conversion in amount.Amount.from_value correctly produces the expected hexadecimal representations.

🧰 Tools
🪛 GitHub Actions: Unit test

[error] 201-201: ERROR: test_from_value_mpt - XRPLBinaryCodecException: 1.2E5 contains fraction value. Please use str type to represent it.

♻️ Duplicate comments (1)
tests/unit/core/binarycodec/types/test_amount.py (1)

204-212: ⚠️ Potential issue

Fix float type handling in test_fractional_MPT_invalid_representation

The test is currently passing a float value (1.1) directly, which is causing an AttributeError because some internal code is trying to call the string method find() on a float object. Since the PR is about improving the error message for MPT float inputs, this test should be modified to properly test the error message.

def test_fractional_MPT_invalid_representation(self):
    self.assertRaises(
        XRPLBinaryCodecException,
        amount.Amount.from_value,
        {
            "mpt_issuance_id": "0000012FFD9EE5DA93AC614B4DB94D7E0FCE415CA51BED47",
-           "value": 1.1,
+           "value": "1.1",  # Test with string representation of float
        },
    )

Alternatively, if the goal is to test that providing a float directly raises an appropriate error message:

def test_fractional_MPT_invalid_representation(self):
-   self.assertRaises(
-       XRPLBinaryCodecException,
-       amount.Amount.from_value,
-       {
-           "mpt_issuance_id": "0000012FFD9EE5DA93AC614B4DB94D7E0FCE415CA51BED47",
-           "value": 1.1,
-       },
-   )
+   try:
+       amount.Amount.from_value({
+           "mpt_issuance_id": "0000012FFD9EE5DA93AC614B4DB94D7E0FCE415CA51BED47",
+           "value": 1.1,  # Using float directly to verify error handling
+       })
+       self.fail("Expected XRPLBinaryCodecException was not raised")
+   except XRPLBinaryCodecException as e:
+       self.assertIn("Please use str type", str(e))
🧰 Tools
🪛 GitHub Actions: Unit test

[error] 205-205: ERROR: test_fractional_MPT_invalid_representation - AttributeError: 'float' object has no attribute 'find'

🧹 Nitpick comments (1)
xrpl/models/amounts/mpt_amount.py (1)

27-27: Consider documenting the breaking change in type annotation

Changing the type of value from Union[str, int, float] to just str is a breaking change that could affect code that previously passed integers or floats. While this appears to be the intended behavior based on the PR objective, it's important to document this change to help users migrate their code.

Consider adding a clarifying comment in the docstring:

    value: str = REQUIRED  # type: ignore
    """
    This field is required.

+   Note: Must be a string representation of the value. Floating point
+   or fractional values should be passed as strings (e.g., "1.5") to
+   maintain precision.
    
    :meta hide-value:
    """
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b1a6ff6 and 4fa0a5f.

📒 Files selected for processing (2)
  • tests/unit/core/binarycodec/types/test_amount.py (2 hunks)
  • xrpl/models/amounts/mpt_amount.py (1 hunks)
🧰 Additional context used
🪛 GitHub Actions: Unit test
xrpl/models/amounts/mpt_amount.py

[error] 6-6: 'typing.Union' imported but unused.

tests/unit/core/binarycodec/types/test_amount.py

[error] 205-205: ERROR: test_fractional_MPT_invalid_representation - AttributeError: 'float' object has no attribute 'find'


[error] 201-201: ERROR: test_from_value_mpt - XRPLBinaryCodecException: 1.2E5 contains fraction value. Please use str type to represent it.


[error] 230-230: FAIL: test_to_json_mpt - AssertionError: Expected JSON output does not match actual output.

⏰ Context from checks skipped due to timeout of 90000ms (12)
  • GitHub Check: Integration test (3.13)
  • GitHub Check: Snippet test (3.13)
  • GitHub Check: Integration test (3.12)
  • GitHub Check: Snippet test (3.12)
  • GitHub Check: Integration test (3.11)
  • GitHub Check: Snippet test (3.11)
  • GitHub Check: Integration test (3.10)
  • GitHub Check: Snippet test (3.10)
  • GitHub Check: Snippet test (3.9)
  • GitHub Check: Integration test (3.9)
  • GitHub Check: Integration test (3.8)
  • GitHub Check: Snippet test (3.8)
🔇 Additional comments (1)
tests/unit/core/binarycodec/types/test_amount.py (1)

100-129:

❓ Verification inconclusive

Fix serialized hex values in MPT test cases

The TODO comment indicates that the serialized values are incorrect. Each new test case is using the same placeholder serialized value, which is causing failures in the tests. These values need to be updated with the correct serialized hex for each test case.


🏁 Script executed:

#!/bin/bash
# Find implementation of MPT value serialization to understand expected format
rg -A 5 "def from_value" --type py | grep -A 15 MPT

Length of output: 53


Based on the fact that the initial search for the MPT serialization implementation produced no output, we need to verify manually that the correct serialization function is in place and determine what the proper hex values should be. The current placeholder value (i.e. the repeated hex string) is clearly causing test failures. Please verify, either by confirming the existence and behavior of the MPT-specific serialization logic or by determining the correct serialized hex values from the expected output.

Action Required: Verify and update the MPT serialized hex values in the following file:

  • File: tests/unit/core/binarycodec/types/test_amount.py (Lines: 100-129)

Ensure that the tests are updated to match the expected output from the actual MPT serialization logic rather than using the same placeholder value for all cases.

value: Union[str, int, float] = REQUIRED # type: ignore
value: str = REQUIRED # type: ignore
Copy link
Collaborator

Choose a reason for hiding this comment

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

I don't think this should be changed at the xrpl package level. Doesn't binary-codec take int or float and eventually convert it to string? cc @mvadari

Copy link
Collaborator

Choose a reason for hiding this comment

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

I didn't review the MPT code - here it is, for reference: https://github.com/XRPLF/xrpl-py/blob/main/xrpl/core/binarycodec/types/amount.py#L304

Copy link
Collaborator

Choose a reason for hiding this comment

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

Oh, looks like it expects a string in _serialize_mpt_amount - https://github.com/XRPLF/xrpl-py/blob/main/xrpl/core/binarycodec/types/amount.py#L273

@ckeshava So I believe your change is correct to only make it a str type.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

  1. We have discrepancies in handling the translation of rippled's std::uint64_t type into a standard JSON type. For instance, asset_price (in OracleSet transaction) accepts an int whereas the MPTAmount.value field accepts str only. Mea culpa, I implemented the PriceOracle amendment.

Is it acceptable to use str JSON type for all the std::uint64_t fields? Do we need to provide any int, float interfaces for these sf<Fieldname> values?

  1. As shown in test cases inside test_amount.py file, we need to support scientific notation syntax in the MPTAmount.value fields. At the moment, scientific notation does not work for serialization.

Copy link
Collaborator

Choose a reason for hiding this comment

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

When I made this change, I just copied IssuedCurrencyAmount.value type which is Union[str, int, float]. @mvadari actually added int/float support to it in this PR - https://github.com/XRPLF/xrpl-py/pull/329/files

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@khancode

raise XRPLBinaryCodecException(f"{mpt_value} is an invalid MPT amount.")

It is not possible to do a string-based search for . character in a float, int input. Do MPT amounts not allow for fractional portion?

It is possible to extend this check for int, float types by processing it through the Decimal library. We can extract the exponent portion of the number and determine the validity of the value.

This was one of the differences I found between serialization of IOU and MPT values. I'd prefer to retain the existing functionality instead of reducing the API surface area (in terms of allowed types for value field). What do you think ?

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.

4 participants