-
Notifications
You must be signed in to change notification settings - Fork 109
Fix DiscriminatedUnionEnvParser to use single parser when only one kind exists #1741
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
base: main
Are you sure you want to change the base?
Conversation
…nd exists When there is exactly one kind defined in a DiscriminatedUnionEnvParser, it should automatically use that parser without requiring the _KIND environment variable to be set. This differs slightly from pydantic functionality but provides better UX. Changes: - Modified from_env() to check parser count when KIND is not set - Added 4 comprehensive tests for the new behavior Co-authored-by: openhands <openhands@all-hands.dev>
Coverage Report •
|
||||||||||||||||||||
all-hands-bot
left a comment
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.
The PR logic looks good overall, but there are important error handling issues that need to be addressed. See inline comments for details.
| else: | ||
| return MISSING | ||
| assert isinstance(kind, str) | ||
| parser = self.parsers[kind] |
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.
🔴 Critical: Missing error handling for invalid KIND values. If a user sets an invalid _KIND (one that doesn't exist in self.parsers), this will raise a KeyError instead of returning MISSING or a meaningful error.
Consider adding validation:
parser = self.parsers.get(kind)
if parser is None:
return MISSINGOr provide a more descriptive error message if you want to fail explicitly.
| kind = next(iter(self.parsers.keys())) | ||
| else: | ||
| return MISSING | ||
| assert isinstance(kind, str) |
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.
🟠 Important: Using assert for validation is problematic because assertions can be disabled with Python's -O flag, and the error message isn't helpful to users. Consider replacing with proper validation:
if not isinstance(kind, str):
return MISSING
all-hands-bot
left a comment
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.
Additional Feedback
🔴 Critical: to_env method has the same KeyError issue
The to_env method at line 294 also has the same KeyError vulnerability:
def to_env(self, key: str, value: Any, output: IO):
parser = self.parsers[value.kind] # Can raise KeyError
parser.to_env(key, value, output)This should also be protected with proper error handling.
🟡 Suggestion: Test Coverage
Consider adding these additional test cases:
- Test invalid KIND value: Verify behavior when
_KINDis set to a non-existent kind - Test single kind with explicit KIND: Ensure backward compatibility when there's one kind but
_KINDis explicitly set - Test to_env with invalid kind: Verify that
to_envhandles invalid kinds gracefully
These would help ensure robust error handling across all code paths.
|
Looks like there are a few issues preventing this PR from being merged!
If you'd like me to help, just leave a comment, like Feel free to include any additional details that might help me get this PR into a better state. You can manage your notification settings |
aaron-seq
left a comment
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.
The PR successfully implements the core feature of automatically using a single parser when only one kind exists, which improves UX. The test coverage is solid with tests for single kind, multiple kinds, and explicit KIND scenarios.
However, there are critical error handling issues that must be addressed before merging:
- Line 287: Missing validation for invalid KIND values - will raise KeyError instead of returning MISSING
- Line 286: Using assert for validation is problematic as it can be disabled with -O flag
These issues were correctly identified by all-hands-bot. Please implement proper error handling using dict.get() or try-except blocks before merging.
| if kind is MISSING: | ||
| return MISSING | ||
| # If there is exactly one kind, use it directly | ||
| if len(self.parsers) == 1: |
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.
The logic here correctly addresses the issue. When there's exactly one parser, using it directly without requiring the _KIND env var improves user experience. However, consider adding validation for edge cases mentioned by all-hands-bot below.
| # ============================================================================ | ||
|
|
||
|
|
||
| def test_discriminated_union_single_kind_uses_parser_directly(clean_env): |
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.
Good test coverage. The test correctly validates that a single kind parser works without requiring the _KIND environment variable. This test confirms the PR's objective is met.
These are issues that have been raised in previous code reviews.
|
|
@OpenHands Please review aaron-seq's comment and tofarr's response. Are there simple changes we can make for clarity which would mean that agents will stop flagging this? |
|
I'm on it! tofarr can track my progress at all-hands.dev |
Replace assert statements with cast() for type narrowing and add explicit comments explaining the intentional behavior: - Use cast(str, kind) instead of assert isinstance(kind, str) for type narrowing after the if-block guarantees kind is a string - Use cast(dict, parser_result) instead of assert isinstance for type narrowing of discriminated union parser results - Add comment explaining that KeyError for invalid KIND values is intentional - typos in env vars should be caught early rather than silently returning MISSING These changes make the code's intent clearer to both human reviewers and automated code analysis tools.
|
I've reviewed aaron-seq's comment and tofarr's response, and made simple changes to improve code clarity so that automated agents will stop flagging these patterns. Changes MadeThe automated agent (all-hands-bot) flagged two issues:
As tofarr explained, both behaviors are intentional:
SolutionI replaced the # Type narrowing: kind is str here (from os.environ.get or dict keys)
kind = cast(str, kind)
# Intentionally raise KeyError for invalid KIND - typos should fail early
parser = self.parsers[kind]
parser_result = parser.from_env(key)
# Type narrowing: discriminated union parsers always return dicts
parser_result = cast(dict, parser_result)This approach:
The changes have been pushed to the |
Summary
This PR modifies the
DiscriminatedUnionEnvParserclass to automatically use the single available parser when there is exactly one kind defined, without requiring the_KINDenvironment variable to be set.Previous behavior: Returns
MISSINGif_KINDis not set, regardless of how many kinds are available.New behavior:
MISSING_KINDenv var required)MISSINGif_KINDis not set (original behavior preserved)_KINDexplicitly set: Uses the specified kind's parser (always works)This differs slightly from standard pydantic functionality but provides better UX when there's an unambiguous single option.
Checklist
@tofarr can click here to continue refining the PR
Agent Server images for this PR
• GHCR package: https://github.com/OpenHands/agent-sdk/pkgs/container/agent-server
Variants & Base Images
eclipse-temurin:17-jdknikolaik/python-nodejs:python3.12-nodejs22golang:1.21-bookwormPull (multi-arch manifest)
# Each variant is a multi-arch manifest supporting both amd64 and arm64 docker pull ghcr.io/openhands/agent-server:d5aef3a-pythonRun
All tags pushed for this build
About Multi-Architecture Support
d5aef3a-python) is a multi-arch manifest supporting both amd64 and arm64d5aef3a-python-amd64) are also available if needed