-
Notifications
You must be signed in to change notification settings - Fork 23
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
Enhance error messages in parameter operations #348
Open
MaxGhenis
wants to merge
10
commits into
PolicyEngine:master
Choose a base branch
from
MaxGhenis:feature/parameter-path-error
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+319
−16
Open
Changes from all commits
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
9c1582d
Enhance error messages in parameter operations
MaxGhenis 0d0d426
Add CLAUDE.md for development documentation
MaxGhenis f0dc6d0
Add emoji to CLAUDE.md title
MaxGhenis 2ed2056
Add Git workflow tips to CLAUDE.md
MaxGhenis 10577c5
Run make format and add formatting guidance to CLAUDE.md
MaxGhenis e5d1318
Update policyengine_core/parameters/operations/get_parameter.py
MaxGhenis 14ea764
Refactor get_parameter to address PR feedback
MaxGhenis 4a2094f
Add PR review and package architecture guidelines to CLAUDE.md
MaxGhenis 42f5db9
Further refactor get_parameter to address PR feedback
MaxGhenis 983c7d1
make format
MaxGhenis File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
# PolicyEngine Core - Development Guide 📚 | ||
|
||
## Common Commands | ||
- `make all`: Full development cycle (install, format, test, build) | ||
- `make install`: Install package in dev mode with dependencies | ||
- `make format`: Format code with Black (79 char line limit) | ||
- `make test`: Run all tests with coverage | ||
- `make mypy`: Run type checking | ||
- `pytest tests/path/to/test_file.py::test_function_name -v`: Run single test | ||
|
||
## Code Style Guidelines | ||
- **Formatting**: Black with 79 character line length | ||
- **Imports**: Standard library, third-party, local modules (standard Python grouping) | ||
- **Types**: Use type annotations, checked with mypy | ||
- **Naming**: snake_case for functions/variables, CamelCase for classes | ||
- **Error Handling**: Use custom error classes from policyengine_core/errors/ | ||
- **Testing**: All new features need tests, both unit and YAML-based tests supported | ||
- **PRs**: Include "Fixes #{issue}" in description, add changelog entry, pass all checks | ||
- **Changelog**: Update changelog_entry.yaml with proper bump type and change description | ||
- **Dependencies**: All versions must be capped to avoid breaking changes | ||
- **Python**: 3.10+ required | ||
|
||
## Git Workflow | ||
- First push to a new branch: `git push -u origin feature/branch-name` to set up tracking | ||
- Subsequent pushes: just use `git push` to update the same PR | ||
- Always run `make format` before committing to ensure code passes style checks | ||
|
||
## PR Reviews | ||
- Check PR comments with: `gh pr view <PR_NUMBER> --repo PolicyEngine/policyengine-core` | ||
- Get review comments with: `gh api repos/PolicyEngine/policyengine-core/pulls/<PR_NUMBER>/comments` | ||
- Address all reviewer feedback before merging | ||
- Follow Clean Code principles when refactoring: | ||
- Keep functions small and focused on a single task | ||
- Avoid deeply nested conditionals and exceptions | ||
- Extract complex logic into well-named helper functions | ||
- Minimize duplication and optimize for readability | ||
- Use consistent error handling patterns | ||
|
||
## Package Architecture | ||
- **Parameter System**: Core framework for tax-benefit system parameters | ||
- Parameters organized in a hierarchical tree (accessible via dot notation) | ||
- Parameters can be scalar values or bracket-based scales | ||
- Supports time-varying values with date-based lookup | ||
- **Errors**: Custom error classes in `policyengine_core/errors/` for specific failures | ||
- **Entities**: Represents different units (person, household, etc.) in microsimulations | ||
- **Variables**: Calculations that can be performed on entities | ||
- **Testing**: Supports both Python tests and YAML-based test files | ||
- **Country Template**: Reference implementation of a tax-benefit system |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
- bump: minor | ||
changes: | ||
added: | ||
- Added ParameterPathError class for specific error handling in parameter operations | ||
changed: | ||
- Completely refactored get_parameter implementation for better readability and maintainability | ||
- Improved error messages with specific details and suggestions for parameter path resolution issues | ||
- Separated parameter path handling into focused helper functions | ||
- Added comprehensive test cases for all error scenarios |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
class ParameterPathError(ValueError): | ||
""" | ||
Exception raised when there's an error in parameter path resolution. | ||
|
||
This includes: | ||
- Parameter not found errors | ||
- Invalid bracket syntax errors | ||
- Invalid bracket index errors | ||
- Attempted bracket access on non-bracket parameters | ||
""" | ||
|
||
def __init__(self, message, parameter_path=None, failed_at=None): | ||
""" | ||
Args: | ||
message (str): The error message. | ||
parameter_path (str, optional): The full parameter path that was being accessed. | ||
failed_at (str, optional): The specific component in the path where the failure occurred. | ||
""" | ||
self.parameter_path = parameter_path | ||
self.failed_at = failed_at | ||
super(ParameterPathError, self).__init__(message) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
import pytest | ||
from policyengine_core.parameters import ParameterNode | ||
from policyengine_core.parameters.operations import get_parameter | ||
from policyengine_core.errors import ParameterPathError | ||
|
||
|
||
def test_parameter_not_found_message(): | ||
"""Test that not found errors have better messages.""" | ||
parameters = ParameterNode( | ||
data={ | ||
"tax": { | ||
"income_tax": { | ||
"rate": { | ||
"values": { | ||
"2022-01-01": 0.2, | ||
} | ||
}, | ||
}, | ||
"sales_tax": { | ||
"rate": { | ||
"values": { | ||
"2022-01-01": 0.1, | ||
} | ||
}, | ||
}, | ||
} | ||
} | ||
) | ||
|
||
# Test parameter not found | ||
with pytest.raises(ParameterPathError) as excinfo: | ||
get_parameter(parameters, "tax.property_tax.rate") | ||
|
||
# Ensure the error message contains useful information | ||
error_message = str(excinfo.value) | ||
assert "property_tax" in error_message | ||
assert "not found" in error_message | ||
assert excinfo.value.parameter_path == "tax.property_tax.rate" | ||
assert excinfo.value.failed_at == "property_tax" | ||
|
||
# Test parameter not found but with similar names | ||
with pytest.raises(ParameterPathError) as excinfo: | ||
get_parameter(parameters, "tax.income") | ||
|
||
# Check that the suggestion for "income" includes "income_tax" | ||
error_message = str(excinfo.value) | ||
assert "income" in error_message | ||
assert "Did you mean" in error_message | ||
assert "income_tax" in error_message | ||
assert excinfo.value.parameter_path == "tax.income" | ||
assert excinfo.value.failed_at == "income" | ||
|
||
|
||
def test_invalid_bracket_syntax_message(): | ||
"""Test error handling with invalid bracket syntax.""" | ||
parameters = ParameterNode( | ||
data={ | ||
"tax": { | ||
"brackets": [ | ||
{ | ||
"threshold": {"values": {"2022-01-01": 0}}, | ||
"rate": {"values": {"2022-01-01": 0.1}}, | ||
}, | ||
], | ||
} | ||
} | ||
) | ||
|
||
# Test invalid bracket syntax (missing closing bracket) | ||
with pytest.raises(ParameterPathError) as excinfo: | ||
get_parameter(parameters, "tax.brackets[0.rate") | ||
|
||
assert "Invalid bracket syntax" in str(excinfo.value) | ||
assert excinfo.value.parameter_path == "tax.brackets[0.rate" | ||
# Don't assert the exact failed_at value since it depends on implementation details | ||
|
||
|
||
def test_bracket_on_non_bracket_parameter(): | ||
"""Test error when trying to use bracket notation on a non-bracket parameter.""" | ||
parameters = ParameterNode( | ||
data={"tax": {"simple_rate": {"values": {"2022-01-01": 0.2}}}} | ||
) | ||
|
||
with pytest.raises(ParameterPathError) as excinfo: | ||
get_parameter(parameters, "tax.simple_rate[0]") | ||
|
||
assert "does not support bracket indexing" in str(excinfo.value) | ||
assert excinfo.value.parameter_path == "tax.simple_rate[0]" | ||
# Don't assert the exact failed_at value since it depends on implementation details |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
unused code, blocking This does not appear to be used anywhere? We're still throwing a ValueError below.