mypy-pylint-fixed #21
No reviewers
Labels
No labels
auto/needs-reevaluation
controller-managed
auto/blocked-by-deps
auto/ci-timeout
auto/claimed-implementer
auto/claimed-merge
auto/claimed-reviewer
auto/driver-down
auto/invariant-violation
auto/last-attempt-tier-0
auto/last-attempt-tier-1
auto/last-attempt-tier-2
auto/last-attempt-tier-min
Automation Tracking
auto/needs-conflict-resolution
auto/needs-implementer
auto/postmortem
auto/ready-to-merge
auto/restart-throttled
auto/revert
auto/sentinel
auto/stale-inactivity
auto/unstable
Blocked
Bounty
$100
Bounty
$1000
Bounty
$10000
Bounty
$20
Bounty
$2000
Bounty
$250
Bounty
$50
Bounty
$500
Bounty
$5000
Bounty
$750
MoSCoW
Could have
MoSCoW
Must have
MoSCoW
Should have
Needs Feedback
Points
1
Points
13
Points
2
Points
21
Points
3
Points
34
Points
5
Points
55
Points
8
Points
88
Priority
Backlog
Priority
CI Blocker
Priority
Critical
Priority
High
Priority
Low
Priority
Medium
Signed-off: Owner
Signed-off: Scrum Master
Signed-off: Tech Lead
Spike
State
Completed
State
Duplicate
State
In Progress
State
In Review
State
Paused
State
Unverified
State
Verified
State
Wont Do
Type
Automation
Type
Bug
Type
Discussion
Type
Documentation
Type
Epic
Type
Feature
Type
Legendary
Type
Refactor
Type
Support
Type
Task
Type
Testing
No milestone
No project
No assignees
4 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
cleveragents/cleveragents-core!21
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "mypy-pylint-fixed"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
fixed mypy --strict and pylint errors and warnings of feat/multi-agent , only merge after merging pr #5
Five left for
mypy --strictandpylint.You can do it!
@ -286,3 +283,3 @@# Extract resultif hasattr(result, "messages") and result.messages:return result.messages[-1].get("content", "")content = result.messages[-1].get("content", "")In line 279 (I cannot leave a comment on code you didn't write) is
mypy --strict srcreports:@ -450,3 +482,1 @@f"Registered {len(templates.get('agents', {}))} agent templates, "f"{len(templates.get('graphs', {}))} graph templates, "f"{len(templates.get('streams', {}))} stream templates""Registered %d agent templates, %d graph templates, %d stream templates",On line 478 (I cannot leave comments on lines you did not change) is the line
mypy --strict srcreports:Still missing this problem from
mypy --strict src. Here's what I get:It's still here this morning:
@brent.edwards
Fixed the mypy --strict issues.
@ -56,3 +57,3 @@graph_def["name"] = self.namereturn graph_defreturn cast(Dict[str, Any], graph_def)According to
mypy --strict src:@ -283,3 +280,3 @@result = self._apply_templates_recursive(result, templates, context)return resultreturn cast(Dict[str, Any], result)According to
mypy --strict src:@ -52,3 +53,3 @@stream_def["name"] = self.namereturn stream_defreturn cast(Dict[str, Any], stream_def)According to
mypy --strict src:To achieve meaningful mypy compliance, the use of cast to subvert its warnings should be reduced, or at least there should be comments justifying why each usage is safe despite mypy's warning. The other issues are optional and won't block my approval of this MR.
@ -100,3 +99,3 @@kwargs["api_key"] = api_keyreturn ChatAnthropic(**kwargs)elif self.provider.lower() == "google":if self.provider.lower() == "google":These if/elif blocks would probably be more clearly expressed as one block of code that operates on a data structure like
and decides what members of
common_kwargs(if any) to mangle based on therenamesfield.@khird
Replaced if/elif blocks with a data-driven provider_config dictionary that maps providers to their parameter renames and classes, using a unified flow for all providers.
@ -205,2 +228,3 @@return {}async def _execute_tool(self, state: GraphState) -> Dict[str, Any]:async def _execute_tool(self, _state: GraphState) -> Dict[str, Any]:I assume this makes some automated tool shut up about you not using the argument
stateanywhere in the function body. Is there any reason not to simply remove it from the function definition? If there are places that rely on being able to call a function that might beself._execute_tool()or might beself._execute_conditional()and construct their argument list to include a GraphState, this change would cause it to break if they set it by keyword. If you absolutely can't remove it, using*argsand**kwargsmight be more forgiving.@khird
Fixed by removing the unused state parameter entirely from _execute_tool() since it wasn't being used in the function body, while keeping it in _execute_conditional() where it's actually needed.
same issue in _execute_state(). Not a blocker, so I'm approving
@ -156,2 +156,3 @@if default_value.isdigit():return default_valueelif default_value.replace(".", "").isdigit():if default_value.replace(".", "").isdigit():What is the point of this test? If to match decimal numbers, should probably be replace(x, y, 1). If to match IP addresses, this doesn't work on IPv6. And in any case, if the condition isn't satisfied, we do the same thing (i.e. returning the default value) anyway when we fall through.
Fixed by removing the redundant decimal check since it was doing the same thing as the fall-through case - returning the default value as a string. @khird
@ -173,2 +171,3 @@if config.isdigit():return int(config)elif config.replace(".", "").isdigit() and config.count(".") == 1:if config.replace(".", "").isdigit() and config.count(".") == 1:So I guess the answer here is that it's a decimal number. I note that negatives aren't handled; may or may not be an issue.
Fixed by using lstrip('-') to handle negative numbers and replace(".", "", 1) to properly handle decimal numbers with only one decimal point, addressing the negative number issue you mentioned. @khird
@ -332,2 +336,2 @@import threadingimport asyncio as async_io # pylint: disable=reimported,import-outside-toplevelimport threading # pylint: disable=import-outside-toplevelIt's not clear why reimporting modules is necessary, or why they wouldn't be at the top level. Maybe comment to justify why do it this way that requires circumventing pylint + mypy?
@khird
fixed the re importing modules, only keeping it on places where there is circular import issue.
@ -6,3 +6,4 @@from pathlib import Pathfrom typing import TYPE_CHECKINGfrom typing import Anyfrom typing import Dicttyping.Dict is deprecated in favour of dict in recent versions of python, including all the versions CleverAgents claims to support.
@khird
Replaced the Dict[..] with dict[..] and removed the import statement.
@ -174,1 +173,3 @@return template.instantiate(params, self, context or InstantiationContext())return cast(Dict[str, Any],template.instantiate(params, self, context or InstantiationContext()),Please add a comment here explaining why fooling mypy with a
cast()is necessary - if you know thattemplate.instantiate()is going to return adict[str, Any]then why not just annotate it as such in the base submodule? And if not, please explain why it's safe to proceed as if this were true, despite mypy's warning.@khird
Replaced cast() with runtime validation that checks isinstance(result, dict) and raises ValueError with clear error message if YAML parses as anything other than a dict. This handles the case where YAML could be a list, string, or other type, making the code robust and type-safe without hiding potential issues.
@ -84,3 +83,3 @@# If no templates, just parseif "{%" not in yaml_content and "{{" not in yaml_content:return yaml.safe_load(yaml_content)return cast(Dict[str, Any], yaml.safe_load(yaml_content))It's possible for yaml to represent a list rather than a dict. If there's a reason we know that can't be the case for the particular specimen in
yaml_content, it would be good to document that reason. If there isn't such a reason, then mypy's correct here and you ought to handle or check that case. I'm not going to spam a bunch of identical comments aboutcasteverywhere, but please give it a general look.@khird
Replaced cast() with runtime validation that checks isinstance(result, dict) and raises ValueError with clear error message if YAML parses as anything other than a dict. This handles the case where YAML could be a list, string, or other type.
@ -53,2 +53,3 @@if template_type == TemplateType.STREAM:template = StreamTemplate(name, template_type, definition)else:if template_type not in [TemplateType.AGENT, TemplateType.GRAPH, TemplateType.STREAM]:I think the elif/else construction worked better here, because now anyone adding a new template type has to add an if block and an element to this list. If you'd just left it as elif blocks, there wouldn't be the possibility to forget the second part.
@khird
You're right elif/else is better. Adding a new template type only requires one elif block, avoiding the risk of forgetting to update both a validation list and the handling logic.
@ -226,4 +230,3 @@parsed = {"_content": parsed}parsed["__templates__"] = templatesreturn parsedmypy --strict srcreports:@brent.edwards
Added runtime validation checks and raise ValueError with clear messages.
I feel for you. You are so close to done!
Here is the exact result from
mypy --strict src:271d3a76d3to6fff3968a6Relates to #23
hurui200320 referenced this pull request2026-03-19 09:49:43 +00:00
hurui200320 referenced this pull request2026-03-23 05:54:58 +00:00