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

Add balance changes parser #383

Merged
merged 35 commits into from Jun 2, 2022
Merged

Add balance changes parser #383

merged 35 commits into from Jun 2, 2022

Conversation

LimpidCrypto
Copy link
Contributor

@LimpidCrypto LimpidCrypto commented May 3, 2022

High Level Overview of Change

The #342 PR will be splitted in four parts. For each exported method (parse_balance_changes, parse_final_balances, parse_previous_balances and parse_order_book_changes) there will now be a separate PR to make it easier to review. This PR adds the functionality to parse the balance changes from a transaction's metadata.

Context of Change

#298

Type of Change

  • New feature (non-breaking change which adds functionality)

Test Plan

I wrote a test to ensure the parser is working well.

  • Add tests that should raise the XRPLTxnFieldsException

@LimpidCrypto
Copy link
Contributor Author

Let's start with parse_balance_changes :)

@JST5000 JST5000 requested review from JST5000 and ckniffen May 3, 2022 21:43
Copy link
Collaborator

@JST5000 JST5000 left a comment

Choose a reason for hiding this comment

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

LGTM

@JST5000 JST5000 requested a review from mvadari May 6, 2022 23:31
@LimpidCrypto
Copy link
Contributor Author

Tests failed because of:
"AssertionError: XRPLRequestFailureException not raised"

Comment on lines 56 to 61
node = (
created_node
if created_node is not None
else modified_node
if modified_node is not None
else deleted_node
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think this could be cleaner using if/else

Suggested change
node = (
created_node
if created_node is not None
else modified_node
if modified_node is not None
else deleted_node
if (created_node is not None):
node = created_node
elif (modified_node is not None):
node = modified_node
else:
node = deleted_node

Copy link
Collaborator

@JST5000 JST5000 May 19, 2022

Choose a reason for hiding this comment

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

Actually, it's probably easier to just set node directly instead of setting created_node/modified_node/deleted_node as intermediary values.

Ex. node = cast(ModifiedNode, affected_node)["ModifiedNode"]

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I would have done so, but unfortunately mypy raises: 'Incompatible types in assignment (expression has type "ModifiedNodeFields", variable has type "CreatedNodeFields")'

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Your suggestion is also not valid. Same error.

Copy link
Collaborator

@JST5000 JST5000 May 23, 2022

Choose a reason for hiding this comment

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

Ah sorry about that. Since node's type isn't inferrable from it's first declaration we have to specify the type explicitly. I tried locally and this version works:

    if diff_type == "CreatedNode":
        node: Union[CreatedNodeFields, ModifiedNodeFields, DeletedNodeFields] = cast(
            CreatedNode, affected_node
        )["CreatedNode"]
    elif diff_type == "ModifiedNode":
        node = cast(ModifiedNode, affected_node)["ModifiedNode"]
    else:
        node = cast(DeletedNode, affected_node)["DeletedNode"]

(Note that this replaces the diff_type if statements AND the node = ... statement)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Works 👍🏻 @mvadari @JST5000 which solution would you prefer? Make CreatedNode, etc. an object (suggestion below; if understood correctly) or keep them like they are and use the solution above? I find the solution above more consistent.

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 have a preference either way. I think the TypedDicts make sense, and that using classes would also make sense. So I'm leaning towards what's easier.

@JST5000
Copy link
Collaborator

JST5000 commented May 19, 2022

Tests failed because of: "AssertionError: XRPLRequestFailureException not raised"

The tests passed once re-ran. I copied the error down and will keep an eye out for the root cause. (Seems to be an intermittent error though, not something related to your changes)

class Fields(TypedDict):
"""Model for possible fields."""

Account: Optional[str]
Copy link
Collaborator

Choose a reason for hiding this comment

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

If all of these are also optional fields, then maybe we should do the same thing as with NormalizedNodes. Might make it easier to type the code as well.

Copy link
Collaborator

@mvadari mvadari May 24, 2022

Choose a reason for hiding this comment

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

Is there any reason you're using TypedDict instead of objects here (for everything except the final return, which I agree should be a dictionary)? It might be easier to type everything, and the constructors wouldn't even have to change.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

So you mean making CreatedNode, ModifiedNode and DeletedNode a dataclass but keep TransactionMetadata as a TypedDict?

Copy link
Collaborator

Choose a reason for hiding this comment

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

I think that since there's no added functions tied to the data, it makes sense to keep them as TypedDicts.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I'm mainly suggesting that we use helper classes because it makes it easier to type optional fields. They can even use the same constructor.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

When making CreatedNode, ModifiedNode and DeletedNode dataclasses I would have to make an extra function that converts the Nodes into theses dataclasses, otherwise mypy throws errors because in _normalize_node I am passing dicts instead of CreatedNode, ... objects. Honestly I don't really think it is easier, but maybe I misunderstood your suggestion.

Copy link
Collaborator

@mvadari mvadari left a comment

Choose a reason for hiding this comment

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

This looks good to me other than the typing issues

Copy link
Collaborator

@JST5000 JST5000 left a comment

Choose a reason for hiding this comment

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

Looks good to me!

@LimpidCrypto LimpidCrypto requested a review from mvadari May 28, 2022 07:07
@mvadari mvadari merged commit fe02f14 into XRPLF:master Jun 2, 2022
@LimpidCrypto LimpidCrypto mentioned this pull request Jun 2, 2022
2 tasks
@LimpidCrypto LimpidCrypto mentioned this pull request Jun 16, 2022
5 tasks
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.

None yet

3 participants