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

Refactor validate params #39

Closed
wants to merge 3 commits into from
Closed

Conversation

Mews
Copy link
Contributor

@Mews Mews commented Jun 18, 2024

Description

Changed the validate_params decorator to get the expected types for each argument from the decorated function's type hints rather than a parameters argument.
The validated arguments are then passed to the decorated function from the json data, or passed as None if they're optional and not present in the json data.

I also changed the tests in test_validate_params to use the new syntax.
I'm currently skipping the tests in TestTupleUnion until I understand what do to to them.

Related Issue

Checklist

Type of change

  • New feature / enhancement
  • Bug fix
  • Documentation
  • Maintenance
  • Other (please specify):

Summary by CodeRabbit

  • New Features

    • Improved parameter validation by extracting parameter types from function type hints.
  • Bug Fixes

    • Fixed issues with the validate_params decorator to improve type validation in various scenarios.
  • Tests

    • Updated tests to reflect changes in parameter type validations.

Copy link
Contributor

coderabbitai bot commented Jun 18, 2024

Walkthrough

This enhancement simplifies parameter validation in the validate_params decorator by leveraging function type hints. It refactors existing code and test cases to extract parameter types directly from function signatures, eliminating the need for specifying types within the decorator.

Changes

Files Change Summary
flask_utils/decorators.py Updated imports, removed a parameter from validate_params, and added functionality to use type hints in decorators.
tests/test_validate_params.py Refactored test cases to specify parameter types directly in function signatures instead of within the decorator.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant FlaskApp
    participant Decorator
    participant Function

    Client->>FlaskApp: Send request with parameters
    FlaskApp->>Decorator: Call validate_params
    Decorator->>Function: Extract type hints from function signature
    Function-->>Decorator: Return validation results
    Decorator-->>FlaskApp: Pass results
    FlaskApp-->>Client: Send response
Loading

Poem

Amidst the code, a rabbit hops,
Refactoring in merry hops,
With type hints now, no room for doubt,
Validations clear, no twist nor rout.
The tests reformed, the code refined,
In flasky bounds, it's well-defined.
🎉🐇


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>.
    • 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 generate interesting stats about this repository and render them as a table.
    • @coderabbitai show all the console.log statements in this repository.
    • @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 as 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.

Additionally, you can add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.

CodeRabbit Configration 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
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: 1

Outside diff range and nitpick comments (2)
flask_utils/decorators.py (2)

Line range hint 92-94: Consider combining nested if statements into a single statement for clarity and efficiency.

- if value in [None, "", [], {}]:
-     if _is_optional(type_hint) or allow_empty:
-         return True
+ if value in [None, "", [], {}] and (_is_optional(type_hint) or allow_empty):
+     return True

Line range hint 254-254: When re-raising exceptions, use raise ... from None to make clear that the re-raise is intentional and not due to a new error during handling.

- raise BadRequestError("The Json Body is malformed.")
+ raise BadRequestError("The Json Body is malformed.") from None

- raise BadRequestError(
-     "The Content-Type header is missing or is not set to application/json, or the JSON body is missing."
- )
+ raise BadRequestError(
+     "The Content-Type header is missing or is not set to application/json, or the JSON body is missing."
+ ) from None

Also applies to: 256-258

Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between 3ec12cf and 6bfddaa.

Files selected for processing (2)
  • flask_utils/decorators.py (4 hunks)
  • tests/test_validate_params.py (9 hunks)
Additional context used
Ruff
flask_utils/decorators.py

92-94: Use a single if statement instead of nested if statements (SIM102)


248-248: Use key in dict instead of key in dict.keys() (SIM118)

Remove .keys()


254-254: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling (B904)


256-258: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling (B904)

Additional comments not posted (3)
flask_utils/decorators.py (2)

5-5: The import of get_type_hints is appropriate given the changes to utilize type hints directly from function signatures.


Line range hint 243-287: The refactoring of the validate_params decorator to use type hints directly from the function signature simplifies the interface and leverages Python's built-in capabilities. Make sure the documentation is updated to reflect these changes.

Tools
Ruff

248-248: Use key in dict instead of key in dict.keys() (SIM118)

Remove .keys()

tests/test_validate_params.py (1)

16-17: The updated usage of @validate_params() in all test methods aligns with the changes in the decorator. Ensure that all relevant test cases are updated or added to cover the new functionality comprehensively.

Also applies to: 56-57, 234-240, 262-263, 285-286, 308-309, 328-329, 348-349, 376-379

flask_utils/decorators.py Outdated Show resolved Hide resolved
coderabbitai[bot]
coderabbitai bot previously approved these changes Jun 18, 2024
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 and nitpick comments (3)
flask_utils/decorators.py (3)

5-5: Add a brief description for the get_type_hints import.

It's a good practice to comment on the purpose of newly added imports, especially when they play a crucial role in the functionality, as get_type_hints does here.


Line range hint 254-254: Improve exception handling by using the raise ... from syntax.

- except BadRequest:
-     raise BadRequestError("The Json Body is malformed.")
+ except BadRequest as err:
+     raise BadRequestError("The Json Body is malformed.") from err

- except UnsupportedMediaType:
-     raise BadRequestError(
-         "The Content-Type header is missing or is not set to application/json, or the JSON body is missing."
-     )
+ except UnsupportedMediaType as err:
+     raise BadRequestError(
+         "The Content-Type header is missing or is not set to application/json, or the JSON body is missing."
+     ) from err

Explicitly chaining exceptions with from provides better context in the traceback, aiding in debugging and understanding the flow of exceptions.

Also applies to: 256-258


Line range hint 18-18: Track the TODO item about error handling in the project management tools.

This is an important consideration for future enhancements. Would you like me to open a GitHub issue to ensure this doesn't get overlooked?

Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between 6bfddaa and bb3e4a4.

Files selected for processing (1)
  • flask_utils/decorators.py (4 hunks)
Additional context used
Ruff
flask_utils/decorators.py

92-94: Use a single if statement instead of nested if statements (SIM102)


254-254: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling (B904)


256-258: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling (B904)

Additional comments not posted (3)
flask_utils/decorators.py (3)

244-250: Efficient use of function type hints to determine parameter types.

This approach leverages Python's type hints to automate parameter validation, reducing redundancy and potential errors from manually specifying types.


248-249: Remove the return type from the parameters dictionary effectively.

This ensures that the return type hint does not interfere with parameter validation.


281-287: Handle optional parameters correctly by setting them to None if not present in the JSON data.

This logic is essential for APIs that need to support optional parameters gracefully.

Copy link
Owner

@Seluj78 Seluj78 left a comment

Choose a reason for hiding this comment

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

Just one comment for now until I have time to test the PR further

@@ -173,7 +173,6 @@ def _check_type(value: Any, expected_type: Type, allow_empty: bool = False, curr


def validate_params(
parameters: Dict[Any, Any],
allow_empty: bool = False,
Copy link
Owner

Choose a reason for hiding this comment

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

Do we still need allow_empty since we have the Optional types ? I use to use allow_empty for empty strings for example but I don't know how typing.Optional handles that

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sorry I don't quite understand :P What does the allow_empty option do?
It means to count empty strings when validating as strings?

Copy link
Owner

Choose a reason for hiding this comment

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

Yeah, the idea was that sometimes I wanted to allow empty strings to be passed to a flask route :)

Copy link
Contributor Author

@Mews Mews Jun 18, 2024

Choose a reason for hiding this comment

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

I mean it should work the same as it did before :P
I didn't change the _check_type function, and the actual format of the data in paramaters hasn't changed because get_type_hints returns a dict in the same format as it was before.

Copy link
Owner

Choose a reason for hiding this comment

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

Okay no worries. I'm writing a bunch of tests anyway to see if what you did does what I want haha, so I'll let you know soon !

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sounds good 👍

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 and nitpick comments (2)
flask_utils/decorators.py (2)

Line range hint 254-254: Use raise ... from None to clarify that these exceptions are not caused by previous errors.

-            except BadRequest:
-                raise BadRequestError("The Json Body is malformed.")
+            except BadRequest as e:
+                raise BadRequestError("The Json Body is malformed.") from None

-            except UnsupportedMediaType:
-                raise BadRequestError(
-                    "The Content-Type header is missing or is not set to application/json, or the JSON body is missing."
-                )
+            except UnsupportedMediaType as e:
+                raise BadRequestError(
+                    "The Content-Type header is missing or is not set to application/json, or the JSON body is missing."
+                ) from None

Also applies to: 256-258


Line range hint 92-94: Consider combining nested if statements into a single if statement for clarity and efficiency.

-    if value in [None, "", [], {}]:
-        if _is_optional(type_hint) or allow_empty:
-            return True
+    if value in [None, "", [], {}] and (_is_optional(type_hint) or allow_empty):
+        return True
Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between bb3e4a4 and 641737d.

Files selected for processing (2)
  • flask_utils/decorators.py (4 hunks)
  • tests/test_validate_params.py (9 hunks)
Files skipped from review as they are similar to previous changes (1)
  • tests/test_validate_params.py
Additional context used
Ruff
flask_utils/decorators.py

92-94: Use a single if statement instead of nested if statements (SIM102)


248-248: Use key in dict instead of key in dict.keys() (SIM118)

Remove .keys()


254-254: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling (B904)


256-258: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling (B904)

Additional comments not posted (2)
flask_utils/decorators.py (2)

5-5: The addition of get_type_hints is consistent with the new approach of extracting type hints directly from the function. This change aligns well with the PR objectives.


248-248: Optimize the dictionary key check by removing .keys().

- if "return" in parameters.keys():
+ if "return" in parameters:

Likely invalid or redundant comment.

Tools
Ruff

248-248: Use key in dict instead of key in dict.keys() (SIM118)

Remove .keys()

coderabbitai[bot]
coderabbitai bot previously approved these changes Jun 18, 2024
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 and nitpick comments (2)
flask_utils/decorators.py (2)

Line range hint 254-254: Improve exception handling by using the raise ... from None syntax to make clear that these are direct raises and not re-raises.

-            except BadRequest:
-                raise BadRequestError("The Json Body is malformed.")
-            except UnsupportedMediaType:
-                raise BadRequestError(
-                    "The Content-Type header is missing or is not set to application/json, or the JSON body is missing."
-                )
+            except BadRequest as e:
+                raise BadRequestError("The Json Body is malformed.") from e
+            except UnsupportedMediaType as e:
+                raise BadRequestError(
+                    "The Content-Type header is missing or is not set to application/json, or the JSON body is missing."
+                ) from e

Also applies to: 256-258


Line range hint 92-94: Refactor nested if statements into a single if statement for clarity and efficiency.

-    if value in [None, "", [], {}]:
-        # Check if type is explicitly Optional or allow_empty is True
-        if _is_optional(type_hint) or allow_empty:
-            return True
+    if value in [None, "", [], {}] and (_is_optional(type_hint) or allow_empty):
+        return True
Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between 641737d and 62bef1e.

Files selected for processing (1)
  • flask_utils/decorators.py (4 hunks)
Additional context used
Ruff
flask_utils/decorators.py

92-94: Use a single if statement instead of nested if statements (SIM102)


254-254: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling (B904)


256-258: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling (B904)

Additional comments not posted (4)
flask_utils/decorators.py (4)

5-5: Updated import to use get_type_hints aligns with the new approach of using type hints directly from the function.


244-250: Correctly extracts type hints from the function and handles the removal of the return type hint.


248-248: The optimization to check dictionary keys directly is already implemented as per previous review.


281-287: Handling of optional parameters and assignment of values to kwargs is clear and correctly implemented.

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 and nitpick comments (1)
flask_utils/decorators.py (1)

Line range hint 254-258: Improve exception handling by using the from keyword for clearer error context.

-            except BadRequest:
-                raise BadRequestError("The Json Body is malformed.")
+            except BadRequest as err:
+                raise BadRequestError("The Json Body is malformed.") from err

-            except UnsupportedMediaType:
-                raise BadRequestError(
-                    "The Content-Type header is missing or is not set to application/json, or the JSON body is missing."
-                )
+            except UnsupportedMediaType as err:
+                raise BadRequestError(
+                    "The Content-Type header is missing or is not set to application/json, or the JSON body is missing."
+                ) from err
Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between 62bef1e and 6a44010.

Files selected for processing (2)
  • flask_utils/decorators.py (4 hunks)
  • tests/test_validate_params.py (9 hunks)
Files skipped from review as they are similar to previous changes (1)
  • tests/test_validate_params.py
Additional context used
Ruff
flask_utils/decorators.py

92-94: Use a single if statement instead of nested if statements (SIM102)


254-254: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling (B904)


256-258: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling (B904)

Additional comments not posted (3)
flask_utils/decorators.py (3)

5-5: LGTM! Using get_type_hints is a Pythonic way to leverage type hints for runtime validation.


244-244: Refactoring to use type hints from the function directly simplifies the interface and reduces redundancy.


248-249: Correctly removing the return type from parameter validation to avoid confusion.

@Seluj78
Copy link
Owner

Seluj78 commented Sep 22, 2024

Superseded by #46

@Seluj78 Seluj78 closed this Sep 22, 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.

2 participants