Skip to content
This repository has been archived by the owner on Nov 19, 2024. It is now read-only.

feat: Now using function parameters #46

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

Conversation

Seluj78
Copy link
Owner

@Seluj78 Seluj78 commented Sep 22, 2024

@Mews @JulienPalard I would love your input/tests on this !

Fixes #30


📚 Documentation preview 📚: https://flask-utils--46.org.readthedocs.build/en/46/

Summary by CodeRabbit

  • New Features

    • Updated the flask_utils module to version 1.0.0, introducing enhanced parameter validation with automatic type extraction from function signatures.
    • Added a new configuration variable, VALIDATE_PARAMS_MAX_DEPTH, to manage validation depth.
  • Bug Fixes

    • Improved error handling for JSON input validation, ensuring clearer error messages for various input scenarios.
  • Documentation

    • Removed outdated documentation for a private API function and updated the documentation to reflect the new configuration variable.
  • Tests

    • Updated test cases to reflect changes in the validate_params decorator and added new tests for error handling and validation depth.

@Seluj78 Seluj78 added the enhancement New feature or request label Sep 22, 2024
@Seluj78 Seluj78 added this to the v1 milestone Sep 22, 2024
@Seluj78 Seluj78 self-assigned this Sep 22, 2024
Copy link
Contributor

coderabbitai bot commented Sep 22, 2024

Walkthrough

The pull request introduces significant updates to the flask_utils module, including a major version increment from "0.7.1" to "1.0.0". Key changes involve the validate_params decorator, which now utilizes function signatures for parameter validation instead of a dictionary format. Documentation for a private function has been removed, and various test cases have been updated to reflect these changes. Additionally, a test file related to error handling has been deleted.

Changes

File Change Summary
flask_utils/__init__.py Updated module version from "0.7.1" to "1.0.0".
flask_utils/decorators.py Modified validate_params to extract parameter types from function signatures; removed allow_empty parameter from _check_type and _is_allow_empty. Added warnings for missing type annotations and conflicts between route and JSON body parameters.
flask_utils/extension.py Introduced VALIDATE_PARAMS_MAX_DEPTH configuration variable for maximum validation depth. Default set to 4.
docs/source/api.rst Removed documentation for flask_utils.decorators._make_optional.
tests/test_validate_params.py Updated test cases to reflect changes in validate_params decorator; added new test classes for various validation scenarios.
tests/test_validate_params_with_error_handlers.py Deleted file containing unit tests for error handling related to JSON request bodies.

Assessment against linked issues

Objective Addressed Explanation
Make validate_params work with function parameters instead of a dict (#30)

Possibly related PRs

🐰 In the garden where bunnies play,
A new version hops in today!
With parameters now clear and bright,
Validation dances in the light.
Errors handled with a cheer,
Let’s celebrate, for spring is here! 🌼✨


Recent review details

Configuration used: .coderabbit.yaml
Review profile: ASSERTIVE

Commits

Files that changed from the base of the PR and between d6f5efc and afa688e.

Files selected for processing (1)
  • flask_utils/extension.py (2 hunks)
Additional comments not posted (4)
flask_utils/extension.py (4)

36-50: Documentation for VALIDATE_PARAMS_MAX_DEPTH looks good!

The added documentation for VALIDATE_PARAMS_MAX_DEPTH is clear and includes a helpful example. The suggestion from the past review comment has been addressed in the following lines (51-54), providing a brief explanation of the functionality.


51-54: Clear explanation of VALIDATE_PARAMS_MAX_DEPTH functionality

The added explanation effectively clarifies the purpose and impact of the VALIDATE_PARAMS_MAX_DEPTH configuration. This addition improves the documentation and addresses the suggestion from the previous review.


130-131: Good addition of default value with explanation

The default value for VALIDATE_PARAMS_MAX_DEPTH is appropriately set using setdefault. The added comment explains the reasoning behind choosing 4 as the default value, which addresses the suggestion from the previous review and improves code maintainability.


Line range hint 36-131: Summary: Changes align well with PR objectives

The introduced changes successfully implement the VALIDATE_PARAMS_MAX_DEPTH configuration variable, which addresses the requirements outlined in issue #30. The modifications allow the validate_params decorator to work more effectively with function parameters by controlling the depth of nested dictionary validation.

Key points:

  1. Clear documentation has been added, including usage examples.
  2. A default value of 4 for VALIDATE_PARAMS_MAX_DEPTH has been set with an explanatory comment.
  3. All suggestions from previous reviews have been addressed.

These changes provide a solid foundation for enhancing the validate_params functionality as intended in the pull request.


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.

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.

Tip

Early access features: enabled

We are currently testing new code review model(s) that may lead to higher noise levels in the review comments. Please disable the early access features if the noise level causes any inconvenience.

Note:

  • You can enable or disable early access features from the CodeRabbit UI or by updating the CodeRabbit configuration file.

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: 11

Outside diff range comments (1)
flask_utils/decorators.py (1)

Line range hint 63-87: Update the docstring to reflect the updated function signature.

The _is_allow_empty function no longer accepts the allow_empty parameter, but the docstring and examples still reference it. Please update the docstring and examples to match the new function signature.

Apply this diff to update the docstring:

 def _is_allow_empty(value: Any, type_hint: Type) -> bool:  # type: ignore
     """Determine if the value is considered empty and whether it's allowed.

     :param value: Value to check.
     :param type_hint: Type hint to check against.

     :return: True if the value is empty and allowed, False otherwise.

     :Example:

     .. code-block:: python

             from typing import Optional
             from flask_utils.decorators import _is_allow_empty

-            _is_allow_empty(None, str, False)  # False
-            _is_allow_empty("", str, False)  # False
-            _is_allow_empty(None, Optional[str], False)  # True
-            _is_allow_empty("", Optional[str], False)  # True
-            _is_allow_empty("", Optional[str], True)  # True
-            _is_allow_empty("", str, True)  # True
-            _is_allow_empty([], Optional[list], False)  # True
+            _is_allow_empty(None, str)  # False
+            _is_allow_empty("", str)  # False
+            _is_allow_empty(None, Optional[str])  # True
+            _is_allow_empty("", Optional[str])  # True
+            _is_allow_empty([], Optional[list])  # True

     .. versionadded:: 0.2.0
     """
Review details

Configuration used: .coderabbit.yaml
Review profile: ASSERTIVE

Commits

Files that changed from the base of the PR and between bfd2b56 and a167f65.

Files selected for processing (5)
  • docs/source/api.rst (0 hunks)
  • flask_utils/init.py (1 hunks)
  • flask_utils/decorators.py (10 hunks)
  • tests/test_validate_params.py (12 hunks)
  • tests/test_validate_params_with_error_handlers.py (0 hunks)
Files not reviewed due to no reviewable changes (2)
  • docs/source/api.rst
  • tests/test_validate_params_with_error_handlers.py
Additional comments not posted (11)
flask_utils/__init__.py (3)

2-2: Request for changelog

For a major version update like this, it's crucial to provide a changelog detailing the changes, especially any breaking changes or new features. If you haven't already, please add a CHANGELOG.md file or update the existing one with information about the changes in version 1.0.0.

To check for an existing changelog, run:

#!/bin/bash
# Description: Check for the existence of a changelog file

# Search for common changelog file names
echo "Searching for changelog files:"
fd -i 'change(s?|log)' -e md -e txt -e rst

# If no changelog is found, remind to create one
if [ $? -ne 0 ]; then
    echo "No changelog file found. Please create a CHANGELOG.md file."
fi

2-2: Significant version bump to 1.0.0

The version has been updated from 0.7.0 to 1.0.0, which is a significant change according to Semantic Versioning. This typically indicates that the library has reached a stable, production-ready state and/or includes breaking changes.

Could you please provide more details about the changes that warrant this major version increment? This information will be crucial for users of the library to understand potential impacts on their projects.

To ensure consistency across the project, please run the following script:

This script will help identify any inconsistencies in version numbers across the project.


2-2: Clarification needed on PR title

The PR title mentions "Now using function parameters", but the changes in this file only reflect a version update. Could you please clarify where the changes related to function parameters are implemented? If they're in other files, it would be helpful to include those in the PR for a comprehensive review.

To search for changes related to function parameters, run:

This will help identify any changes to function signatures across the project.

tests/test_validate_params.py (7)

1-1: LGTM! Decorator usage updated and type annotations added.

The changes to the validate_params decorator usage and the addition of type annotations in the function signature represent a shift in the validation approach. This new method seems more intuitive and aligns with Python's type hinting system.

Also applies to: 18-19


61-62: LGTM! Improved type annotations in function signature.

The updated default_types function now uses explicit type annotations for all parameters, which aligns with the new validation approach. This change improves code readability and provides clear expectations for the function parameters.


266-267: LGTM! Proper use of Union type.

The updated union function now uses the Union type from the typing module, which is the recommended way to specify multiple allowed types. This change aligns with the new validation approach and improves code consistency.


289-290: LGTM! Proper use of Optional type. Query about commented test.

The updated optional function now uses the Optional type from the typing module, which correctly specifies that the age parameter can be of type int or None. This aligns with the new validation approach.

There's a commented-out test case:

# response = client.post("/optional", json={"name": "John", "age": 25})
# assert response.status_code == 200

Was this intentionally removed? If so, consider adding a comment explaining why, or remove it entirely if it's no longer needed.

Also applies to: 294-295


312-313: LGTM! Consistent application of type annotations.

The changes in the TestList, TestDict, and TestAny classes demonstrate a consistent application of the new validation approach. The use of List[str], Dict[str, int], and Any types from the typing module is correct and improves type checking and code readability.

Also applies to: 332-333, 352-353


380-383: LGTM! Comprehensive demonstration of type annotations.

The updated mix_and_match function signature showcases a comprehensive use of various type annotations, including Union, Optional, List, Dict, and Any. This demonstrates the flexibility and power of the new validation approach in handling complex parameter types.


Line range hint 1-567: Overall, excellent improvements to parameter validation and test coverage.

The changes in this file represent a significant enhancement to the parameter validation approach:

  1. Consistent use of type annotations in function signatures, leveraging Python's type hinting system.
  2. Removal of validation logic from the decorator, simplifying its usage.
  3. Addition of comprehensive test cases covering various scenarios, including error handling, warnings, and edge cases.

These improvements will lead to more robust and maintainable code, with clearer expectations for function parameters and better error handling. The enhanced test suite will help catch potential issues early in the development process.

flask_utils/decorators.py (1)

1-2: Appropriate addition of required imports.

The addition of inspect and warnings modules is appropriate, as they are used for function signature introspection and issuing warnings within the code.

tests/test_validate_params.py Show resolved Hide resolved
tests/test_validate_params.py Show resolved Hide resolved
tests/test_validate_params.py Show resolved Hide resolved
tests/test_validate_params.py Show resolved Hide resolved
tests/test_validate_params.py Show resolved Hide resolved
flask_utils/decorators.py Show resolved Hide resolved
flask_utils/decorators.py Show resolved Hide resolved
@@ -156,8 +134,9 @@
"""

if curr_depth >= VALIDATE_PARAMS_MAX_DEPTH:
warnings.warn(f"Maximum depth of {VALIDATE_PARAMS_MAX_DEPTH} reached.", SyntaxWarning, stacklevel=2)
Copy link
Contributor

@coderabbitai coderabbitai bot Sep 22, 2024

Choose a reason for hiding this comment

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

Use a more suitable warning category for depth limit reached.

The warning issued when the maximum depth is reached uses SyntaxWarning. Since this is a runtime behavior concern, consider using RuntimeWarning to more accurately reflect the nature of the warning.

Apply this diff to change the warning category:

- warnings.warn(f"Maximum depth of {VALIDATE_PARAMS_MAX_DEPTH} reached.", SyntaxWarning, stacklevel=2)
+ warnings.warn(f"Maximum depth of {VALIDATE_PARAMS_MAX_DEPTH} reached.", RuntimeWarning, stacklevel=2)
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
warnings.warn(f"Maximum depth of {VALIDATE_PARAMS_MAX_DEPTH} reached.", SyntaxWarning, stacklevel=2)
warnings.warn(f"Maximum depth of {VALIDATE_PARAMS_MAX_DEPTH} reached.", RuntimeWarning, stacklevel=2)

Copy link
Owner Author

Choose a reason for hiding this comment

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

I'm not sure about this, @JulienPalard what do you think should be raised as a warning here ? It's the first time I'm using the warnings module

Copy link
Contributor

Choose a reason for hiding this comment

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

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

Comment on lines +270 to +308
f"Parameter {key} is defined in both the route and the JSON body. "
f"The JSON body will override the route parameter.",
SyntaxWarning,
stacklevel=2,
)
Copy link
Contributor

@coderabbitai coderabbitai bot Sep 22, 2024

Choose a reason for hiding this comment

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

Use UserWarning for parameter override warnings.

When warning about parameters defined in both the route and the JSON body, SyntaxWarning is used. Since this situation pertains to user code and potential logic issues rather than syntax problems, consider using UserWarning instead.

Apply this diff:

 warnings.warn(
     f"Parameter {key} is defined in both the route and the JSON body. "
     f"The JSON body will override the route parameter.",
-    SyntaxWarning,
+    UserWarning,
     stacklevel=2,
 )
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
f"Parameter {key} is defined in both the route and the JSON body. "
f"The JSON body will override the route parameter.",
SyntaxWarning,
stacklevel=2,
)
f"Parameter {key} is defined in both the route and the JSON body. "
f"The JSON body will override the route parameter.",
UserWarning,
stacklevel=2,
)

Copy link
Owner Author

Choose a reason for hiding this comment

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

Copy link
Contributor

Choose a reason for hiding this comment

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

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

Comment on lines +263 to +298
warnings.warn(f"Parameter {name} has no type annotation.", SyntaxWarning, stacklevel=2)
expected_params[name] = Any
Copy link
Contributor

@coderabbitai coderabbitai bot Sep 22, 2024

Choose a reason for hiding this comment

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

Consider enforcing type annotations for parameters.

Parameters without type annotations are assigned Any, which may lead to unintended behavior during validation. It might be beneficial to enforce type annotations by raising an error or providing a clear message to the developer to add the missing annotations.

Alternatively, if retaining Any, consider updating the warning to a more appropriate category:

- warnings.warn(f"Parameter {name} has no type annotation.", SyntaxWarning, stacklevel=2)
+ warnings.warn(f"Parameter {name} has no type annotation.", UserWarning, stacklevel=2)
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
warnings.warn(f"Parameter {name} has no type annotation.", SyntaxWarning, stacklevel=2)
expected_params[name] = Any
warnings.warn(f"Parameter {name} has no type annotation.", UserWarning, stacklevel=2)
expected_params[name] = Any

Copy link
Owner Author

Choose a reason for hiding this comment

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

Same thing here @JulienPalard, not sure how to use the warnings module 🤔

Copy link
Contributor

Choose a reason for hiding this comment

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

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

@Seluj78 Seluj78 mentioned this pull request Sep 22, 2024
10 tasks
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: 6

Outside diff range comments (1)
flask_utils/decorators.py (1)

Line range hint 63-88: Update the docstring of _is_allow_empty to reflect the updated function signature.

The parameter allow_empty has been removed from _is_allow_empty, but the docstring still references it in the parameters and examples. Please update the docstring to prevent confusion.

Review details

Configuration used: .coderabbit.yaml
Review profile: ASSERTIVE

Commits

Files that changed from the base of the PR and between a167f65 and ef9babd.

Files selected for processing (2)
  • flask_utils/decorators.py (10 hunks)
  • tests/test_validate_params.py (12 hunks)

flask_utils/decorators.py Outdated Show resolved Hide resolved
flask_utils/decorators.py Outdated Show resolved Hide resolved
flask_utils/decorators.py Show resolved Hide resolved
tests/test_validate_params.py Outdated Show resolved Hide resolved
tests/test_validate_params.py Show resolved Hide resolved
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: 3

Review details

Configuration used: .coderabbit.yaml
Review profile: ASSERTIVE

Commits

Files that changed from the base of the PR and between ef9babd and 47bf4a3.

Files selected for processing (2)
  • flask_utils/decorators.py (10 hunks)
  • tests/test_validate_params.py (12 hunks)
Additional comments not posted (12)
tests/test_validate_params.py (12)

19-20: Use of explicit type annotations enhances readability

The bad_format function now uses explicit type annotations for its parameters, which improves code clarity and leverages Python's type checking capabilities.


244-245: Tuple type annotation is appropriate for testing

The use of a tuple (str, int) in the type annotation of the union function adequately tests the functionality for accepting multiple types.


267-268: Appropriate use of Union type

The union function correctly uses the Union type from the typing module to accept either a str or an int for the name parameter.


290-291: Correct implementation of optional parameter

The optional function properly uses Optional[int] to indicate that the age parameter is optional and can be of type int or None.


313-314: Proper usage of List type annotation

The list function correctly uses List[str] to specify that name should be a list of strings.


333-334: Correct usage of Dict type annotation

The dict_route function properly uses Dict[str, int] to require name to be a dictionary with string keys and integer values.


353-354: Appropriate use of Any type

The any_route function uses Any for the name parameter, allowing it to accept any type, which is suitable for testing the flexibility of the validator.


381-384: Comprehensive type annotations in mix_and_match function

The mix_and_match function effectively combines various type annotations, including Union, Optional, List, Dict, and Any, to test multiple scenarios.


507-540: Effective testing of annotation warnings

The TestAnnotationWarnings class correctly tests for warnings when parameters lack type annotations and when there are duplicate keys between route and JSON body parameters.


548-548: Deeply nested type annotations test max depth handling

The example function in TestMaxDepth uses a deeply nested Dict type to test the maximum depth warning functionality effectively.


574-577: Testing JSON body overriding route parameters

The create_user function is designed to test the behavior when a parameter in the JSON body overrides a route parameter, which is important for understanding potential conflicts.


590-591: Correct handling of optional parameters with None values

The empty_route function properly handles the name parameter when it is None or an empty string, ensuring that optional parameters are managed correctly.

flask_utils/decorators.py Show resolved Hide resolved
flask_utils/decorators.py Show resolved Hide resolved
tests/test_validate_params.py Show resolved Hide resolved
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: 4

Outside diff range comments (1)
flask_utils/extension.py (1)

Implementation for function parameters is missing.

The introduction of VALIDATE_PARAMS_MAX_DEPTH is properly implemented and documented. However, the primary objective outlined in the PR description—to modify the validate_params decorator to work directly with function parameters instead of requiring a dictionary format—is not addressed in the flask_utils/decorators.py file.

  • The validate_params decorator still validates JSON body parameters as per its current implementation.
Analysis chain

Line range hint 1-95: Overall changes look good, but implementation for function parameters is missing.

The introduction of VALIDATE_PARAMS_MAX_DEPTH and its documentation are well-implemented. However, the main objective mentioned in the PR description - modifying validate_params to work directly with function parameters instead of requiring a dictionary format - is not addressed in this file.

To ensure we're not missing any relevant changes, let's check for modifications to the validate_params decorator:

If the implementation is indeed missing, consider updating the PR description or adding a TODO comment to track this pending change.

Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for changes to the validate_params decorator
rg --type python -A 10 'def validate_params'

Length of output: 77


Script:

#!/bin/bash
# Search for changes to the validate_params decorator in Python files
rg -g "*.py" 'def validate_params' -A 10

Length of output: 780

Review details

Configuration used: .coderabbit.yaml
Review profile: ASSERTIVE

Commits

Files that changed from the base of the PR and between 47bf4a3 and f1049aa.

Files selected for processing (3)
  • flask_utils/decorators.py (9 hunks)
  • flask_utils/extension.py (2 hunks)
  • tests/test_validate_params.py (12 hunks)
Additional context used
Learnings (1)
tests/test_validate_params.py (1)
Learnt from: Seluj78
PR: Seluj78/flask-utils#46
File: tests/test_validate_params.py:244-245
Timestamp: 2024-09-22T14:16:35.584Z
Learning: In tests that are specifically designed to test tuple type annotations (e.g., testing TupleUnion), use the tuple type notation (e.g., `(str, int)`) rather than `Union`.
Additional comments not posted (16)
flask_utils/decorators.py (5)

1-2: Appropriate Addition of Imports

The inclusion of import inspect and import warnings is necessary for the new functionality introduced in the code, such as accessing function signatures and issuing warnings.


131-131: Dynamic Retrieval of Validation Depth Configuration

Retrieving max_depth from current_app.config allows the maximum validation depth to be configurable, enhancing the flexibility of the decorator for different application needs.


Line range hint 167-245: Updated Documentation Reflects Simplified Decorator Usage

The revised docstring for validate_params accurately describes the new behavior, emphasizing that the decorator now automatically reads parameter types from the function signature without requiring explicit arguments. This improves developer experience by simplifying usage.


279-289: Simplification by Extracting Parameters from Function Signature

By extracting expected parameters directly from the function's signature, the code eliminates the need for manual parameter specification. This change reduces redundancy and potential errors, making the decorator more intuitive to use.


331-332: Ensuring Correct Argument Passing to Wrapped Function

Updating kwargs with provided_values ensures that the validated parameters are correctly passed to the original function. This maintains the integrity of the original function's expected arguments after validation.

tests/test_validate_params.py (11)

19-20: LGTM! Transition to explicit parameter annotations is correct.

The function bad_format now uses explicit type annotations, and the @validate_params() decorator is properly applied without parameters.


62-63: LGTM! Function default_types updated with type annotations.

The function parameters are now explicitly annotated with their respective types, enhancing clarity and validation.


244-245: Correct use of tuple type annotation for testing TupleUnion.

Using the tuple (str, int) for the parameter name in the union function is appropriate for testing TupleUnion functionality as intended.


267-268: Function union correctly uses Union type annotation.

The parameter name is properly annotated with Union[str, int], allowing it to accept either a string or an integer.


290-291: Function optional correctly uses Optional type annotation.

The parameter age is correctly annotated with Optional[int], indicating that it can be an int or None.


333-334: Function dict_route correctly uses Dict type annotation.

The parameter name is appropriately annotated with Dict[str, int], ensuring it accepts a dictionary with string keys and integer values.


353-354: Function any_route correctly uses Any type annotation.

The parameter name is correctly annotated with Any, allowing acceptance of any data type for comprehensive testing.


381-384: Function mix_and_match correctly combines various type annotations.

Parameters are properly annotated using Union, Optional, List, Dict, and Any, effectively demonstrating mixed type validations.


512-513: Appropriate test for missing type annotations.

The function example intentionally leaves the name parameter without a type annotation to test the warning mechanism, which is correctly handled in the test cases.


606-607: Function empty_route correctly handles optional parameters.

The parameter name is appropriately annotated with Optional[str], allowing it to be omitted or set to None, which is correctly tested.


590-593: Potential issue with route parameter being overridden by JSON body.

In the create_user function, the route parameter user_id could be overridden by a user_id key in the JSON body, which may lead to unexpected behavior or security concerns.

Consider modifying the function to prevent the JSON body from overriding route parameters. One approach is to exclude user_id from the JSON payload or rename the parameter in the JSON body.

Apply this diff to adjust the function:

 @flask_client.post("/users/<int:user_id>")
 @validate_params()
-def create_user(user_id: int):
+def create_user(user_id: int, data: dict):
     return f"{user_id}"

And update the test to avoid passing user_id in the JSON:

 def test_valid_request(self, client):
-    response = client.post("/users/123", json={"user_id": 456})
+    response = client.post("/users/123", json={"name": "John"})
     assert response.status_code == 200
-    assert response.text == "456"
+    assert response.text == "123"

Likely invalid or redundant comment.

tests/test_validate_params.py Show resolved Hide resolved
tests/test_validate_params.py Show resolved Hide resolved
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 (1)
flask_utils/__init__.py (1)

Documentation updates for validate_params are incomplete.

While validate_params is referenced in README.md, its internal docstrings have not been updated to reflect recent changes. Please ensure that all docstrings and relevant documentation are revised to align with the major version update.

Analysis chain

Line range hint 1-35: Verify documentation updates for major version change.

Given the significant version increment and the changes mentioned in the PR objectives, it's crucial to ensure that the documentation has been updated to reflect the new usage of validate_params.

Please run the following script to check for documentation updates:

Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify documentation updates for validate_params

# Test 1: Check for changes in README or documentation files
echo "Checking for changes in README or documentation files:"
rg --type md --type rst 'validate_params'

# Test 2: Look for updates in docstrings of validate_params
echo "Checking for updates in validate_params docstring:"
ast-grep --lang python --pattern $'def validate_params($$$):
  """
  $$$
  """
  $$$'

# Test 3: Check for changes in example usage in documentation
echo "Checking for changes in example usage in documentation:"
rg --type md --type rst '@validate_params'

Length of output: 857


Script:

#!/bin/bash
# Description: Verify documentation updates for validate_params

# Test 1: Check for mentions of 'validate_params' in README or documentation files
echo "Checking for mentions of 'validate_params' in documentation files:"
rg --type md --type rst 'validate_params'

# Test 2: Look for updates in docstrings of validate_params with multiline support
echo "Checking for updates in validate_params docstrings:"
ast-grep --lang python --pattern $'def validate_params($$$):
    """
    $$$
    """
    $$$'

# Test 3: Check for example usage of validate_params in documentation with multiline mode
echo "Checking for example usage of 'validate_params' in documentation:"
rg --type md --type rst -U '@validate_params'

Length of output: 861

Review details

Configuration used: .coderabbit.yaml
Review profile: ASSERTIVE

Commits

Files that changed from the base of the PR and between f1049aa and d6f5efc.

Files selected for processing (6)
  • docs/source/api.rst (0 hunks)
  • flask_utils/init.py (1 hunks)
  • flask_utils/decorators.py (9 hunks)
  • flask_utils/extension.py (2 hunks)
  • tests/test_validate_params.py (12 hunks)
  • tests/test_validate_params_with_error_handlers.py (0 hunks)
Files not reviewed due to no reviewable changes (2)
  • docs/source/api.rst
  • tests/test_validate_params_with_error_handlers.py
Additional context used
Learnings (1)
tests/test_validate_params.py (1)
Learnt from: Seluj78
PR: Seluj78/flask-utils#46
File: tests/test_validate_params.py:244-245
Timestamp: 2024-09-22T14:16:35.584Z
Learning: In tests that are specifically designed to test tuple type annotations (e.g., testing TupleUnion), use the tuple type notation (e.g., `(str, int)`) rather than `Union`.
Additional comments not posted (13)
flask_utils/__init__.py (1)

2-2: Version update approved, but verify major changes.

The version update from 0.7.1 to 1.0.0 is consistent with Semantic Versioning for a major change. This aligns with the PR objectives to modify the validate_params decorator's functionality.

To ensure the major version bump is justified, please run the following script to verify the implementation of the validate_params changes:

#!/bin/bash
# Description: Verify changes to validate_params decorator

# Test 1: Check for changes in the validate_params implementation
echo "Checking for changes in validate_params implementation:"
rg --type python -p 'def validate_params' -A 10

# Test 2: Look for usage examples of the new validate_params syntax
echo "Checking for new usage examples of validate_params:"
rg --type python '@validate_params\s*\n\s*def'

# Test 3: Check for updates in docstrings or comments related to validate_params
echo "Checking for updates in docstrings or comments:"
rg --type python -i 'validate_params.*?("""|\'\'\')' -A 5
flask_utils/extension.py (1)

Line range hint 1-125: LGTM! Changes align well with PR objectives

The introduction of VALIDATE_PARAMS_MAX_DEPTH configuration, its documentation, and default value setting are well-implemented and consistent with the PR objectives. These changes effectively address the goals outlined in issue #30, allowing for more flexible parameter validation depth control.

The implementation looks correct, and the documentation provides clear guidance on how to use the new configuration. Good job on improving the validate_params functionality!

flask_utils/decorators.py (3)

1-2: Imports of inspect and warnings are appropriate

The added imports are necessary for utilizing inspect.signature and issuing warnings within the code.


200-201: Example correctly demonstrates the updated validate_params usage

The example now illustrates how to use @validate_params() without parameters, relying on function annotations for validation. This makes the decorator more intuitive and user-friendly.


225-228: Documentation effectively warns about parameter overriding

Including a warning in the documentation about JSON body parameters overriding route parameters enhances clarity and helps prevent unexpected behavior for users.

tests/test_validate_params.py (8)

1-1: LGTM!

The imports have been appropriately added to support the new tests and functionalities.

Also applies to: 9-10


19-20: LGTM!

The validate_params decorator is correctly applied, and the function uses explicit type annotations.


244-245: LGTM!

The function union correctly uses tuple type annotations for testing purposes.


267-268: LGTM!

The use of Union type annotation in the union function is appropriate and enhances type flexibility.


290-291: LGTM!

The optional function accurately utilizes Optional type annotation for the age parameter.


333-334: LGTM!

The dict_route function correctly employs Dict type annotation for the name parameter.


353-354: LGTM!

The any_route function properly uses Any type annotation, allowing for flexible input types.


381-384: LGTM!

The mix_and_match function effectively combines multiple type annotations, showcasing comprehensive testing of type validations.

flask_utils/extension.py Show resolved Hide resolved
flask_utils/extension.py Show resolved Hide resolved
Comment on lines +226 to +227
If a parameter exists both in the route parameters and in the JSON body,
the value from the JSON body will override the route parameter. A warning
Copy link

@JulienPalard JulienPalard Sep 22, 2024

Choose a reason for hiding this comment

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

Ohhhh this is interesting, it is not what I would expect, and it even feels like a security issue to me.

I bet this can be exploited, at the very least least to hide malicious requests in the logs (appearing with the wrong id), like me doing:

curl -XGET 0:8000/users/12 -H "Content-Type: application/json" -d '{"user_id": 1}'

being me (user id 12) looking like I look at my profile but in fact receiving the profile of the admin. Yes I know mixing a GET and a request body is debatable (challenge: let's not debate it right now).

or

 curl -XGET 0:8000/users/12 -H "Content-Type: application/json" -d '{"user_id": "' OR 1 = 1 -- "}'

being me doing SQL injections on the user id while still producing clean logs :D

I would prefer having the route segment win, for "consistency" between a request having no body and a request having one.

There's a way to have this fixed by design: have the body parameter being passed as an argument named body instead, I mean:

def create_user(user_id: int, body: TypedDict('User', {'user_id': int, "username": str}):
    ...

this way one can do the (legitimate):

curl -XPUT 0:8000/users/12 -H "Content-Type: application/json" -d '{"user_id": 12, "username": "mdk"}'

(we would see this kind of request in the GET / modify locally / PUT way of altering documents).

and even have an assert user_id == body["user_id"] as they would not be mixed.

Ohhhh this mean that you could have:

class User(TypedDict):
    user_id: int
    username: str

def update_user(user_id: int, body: User):
    ...

which is a tad longer to write, but probably way way more readable for big bodies (having a big class instead of a big list of arguments).

This would allow to reuse types too, I bet the User typeddict could be used in many places.

Another slightly different way would be to use **kwargs to provide the request body:

def update_user(user_id: int, **kwargs: Unpack[User]):
    ...

but this have to be a design choice: using kwargs one cannot call a method with the same name in the body and the route, as it would lead to a call like update_user(12, user_id=42) (which would TypeError with got multiple values for argument 'user_id'. It would not reduce the API design choices, the developper would just have to rename the route argument to have it pass.

I have to cook now, I'm french, and I'll have to digest what I just wrote too, let's speak about it later.

Copy link
Owner Author

@Seluj78 Seluj78 Sep 22, 2024

Choose a reason for hiding this comment

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

Oh wow, that's a lot of stuff to unpack (lol), thanks a lot for the suggestions, I'll think about it as well on my side and we can try and find a middleground later.

But from my first read of your answer, I do agree that it's a security risk, and that another way needs to be found ! And I love the idea of classes, it would make typing and reusing much easier ! Thought might feel like double the work when also using an ORM or PyDantic (which I've just started using) ?

Choose a reason for hiding this comment

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

ORM

Beware, you cannot easily auto-generate those document types from an ORM, it would imply that modifying the DB modifies the API, maybe not what you want every time. You'd also need a kind of way to chose which DB fields to expose and which not to expose, which one to expose read-only, which one are "relations" that should automagically be converted to hyperlinks, and so on and so on, that's a whole other project.

Copy link
Owner Author

Choose a reason for hiding this comment

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

Yes of course !

My thought was rather on how similar it would look from openapi definitions ! That might be something I can do in the future 👀 when you use this extension, you can auto generate a swagger 🥰

Copy link
Owner Author

Choose a reason for hiding this comment

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

But anyway, I do like the idea of explicit parameters inside the function, that way you don't have to unpack a body variable, which comes back to body = request.get_json()

Copy link
Owner Author

Choose a reason for hiding this comment

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

I could also probably support multiple ways of passing parameters to the function? 🤔

Copy link
Owner Author

Choose a reason for hiding this comment

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

Choose a reason for hiding this comment

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

Hehe, if it already exists, better not doing it again: solving a problem with 0 lines of code :D

I'm really not a flask user, and probably almost never used pydantic, so I'll refrain to compare your solution and Flask-Pydantic, I hope you have a better overview than mine here.

I do appreciate that they properly name body and query parameters though.

/me angrily looks at PHP about it calling the query string GET and the request body POST.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Make validate_params work with the function's parametters instead of a dict
2 participants