chore(core): bump mypy version to 1.17 (#32390)

Co-authored-by: Mason Daugherty <mason@langchain.dev>
This commit is contained in:
Christophe Bornet 2025-08-07 19:26:29 +02:00 committed by GitHub
parent 42c1159991
commit 499dc35cfb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 676 additions and 566 deletions

View File

@ -144,7 +144,7 @@ def beta(
obj.__init__ = functools.wraps(obj.__init__)( # type: ignore[misc]
warn_if_direct_instance
)
return cast("T", obj)
return obj
elif isinstance(obj, property):
# note(erick): this block doesn't seem to be used?

View File

@ -225,7 +225,7 @@ def deprecated(
obj.__init__ = functools.wraps(obj.__init__)( # type: ignore[misc]
warn_if_direct_instance
)
return cast("T", obj)
return obj
elif isinstance(obj, FieldInfoV1):
wrapped = None

View File

@ -107,23 +107,27 @@ class _StreamingParser:
self.buffer = ""
# yield all events
try:
for event, elem in self.pull_parser.read_events():
if event == "start":
# update current path
self.current_path.append(elem.tag)
self.current_path_has_children = False
elif event == "end":
# remove last element from current path
#
self.current_path.pop()
# yield element
if not self.current_path_has_children:
yield nested_element(self.current_path, elem)
# prevent yielding of parent element
if self.current_path:
self.current_path_has_children = True
else:
self.xml_started = False
for raw_event in self.pull_parser.read_events():
if len(raw_event) <= 1:
continue
event, elem = raw_event
if isinstance(elem, ET.Element):
if event == "start":
# update current path
self.current_path.append(elem.tag)
self.current_path_has_children = False
elif event == "end":
# remove last element from current path
#
self.current_path.pop()
# yield element
if not self.current_path_has_children:
yield nested_element(self.current_path, elem)
# prevent yielding of parent element
if self.current_path:
self.current_path_has_children = True
else:
self.xml_started = False
except xml.etree.ElementTree.ParseError:
# This might be junk at the end of the XML input.
# Let's check whether the current path is empty.

View File

@ -5300,7 +5300,7 @@ class RunnableBindingBase(RunnableSerializable[Input, Output]):
kwargs.
"""
config: RunnableConfig = Field(default_factory=RunnableConfig) # type: ignore[arg-type]
config: RunnableConfig = Field(default_factory=RunnableConfig)
"""The config to bind to the underlying Runnable."""
config_factories: list[Callable[[RunnableConfig], RunnableConfig]] = Field(

View File

@ -5,7 +5,7 @@ import inspect
import typing
from collections.abc import AsyncIterator, Iterator, Sequence
from functools import wraps
from typing import TYPE_CHECKING, Any, Optional, Union
from typing import TYPE_CHECKING, Any, Optional, Union, cast
from pydantic import BaseModel, ConfigDict
from typing_extensions import override
@ -397,7 +397,7 @@ class RunnableWithFallbacks(RunnableSerializable[Input, Output]):
)
)
to_return = {}
to_return: dict[int, Union[Output, BaseException]] = {}
run_again = dict(enumerate(inputs))
handled_exceptions: dict[int, BaseException] = {}
first_to_raise = None
@ -447,7 +447,7 @@ class RunnableWithFallbacks(RunnableSerializable[Input, Output]):
if not return_exceptions and sorted_handled_exceptions:
raise sorted_handled_exceptions[0][1]
to_return.update(handled_exceptions)
return [output for _, output in sorted(to_return.items())] # type: ignore[misc]
return [cast("Output", output) for _, output in sorted(to_return.items())]
@override
def stream(
@ -569,7 +569,7 @@ class RunnableWithFallbacks(RunnableSerializable[Input, Output]):
async for chunk in stream:
yield chunk
try:
output = output + chunk
output = output + chunk # type: ignore[operator]
except TypeError:
output = None
except BaseException as e:

View File

@ -177,7 +177,7 @@ class RunLog(RunLogPatch):
# Then compare that the ops are the same
return super().__eq__(other)
__hash__ = None # type: ignore[assignment]
__hash__ = None
T = TypeVar("T")

View File

@ -277,7 +277,7 @@ def _convert_any_typed_dicts_to_pydantic(
)
fields: dict = {}
for arg, arg_type in annotations_.items():
if get_origin(arg_type) is Annotated:
if get_origin(arg_type) is Annotated: # type: ignore[comparison-overlap]
annotated_args = get_args(arg_type)
new_arg_type = _convert_any_typed_dicts_to_pydantic(
annotated_args[0], depth=depth + 1, visited=visited

View File

@ -417,7 +417,7 @@ class AIMessageChunk(AIMessage):
self._tool_call_chunks = [
block for block in self.content if types.is_tool_call_chunk(block)
]
return cast("list[types.ToolCallChunk]", self._tool_call_chunks)
return self._tool_call_chunks
@property
def tool_calls(self) -> list[types.ToolCall]:

View File

@ -28,7 +28,7 @@ repository = "https://github.com/langchain-ai/langchain"
[dependency-groups]
lint = ["ruff<0.13,>=0.12.2"]
typing = [
"mypy<1.16,>=1.15",
"mypy<1.18,>=1.17.1",
"types-pyyaml<7.0.0.0,>=6.0.12.2",
"types-requests<3.0.0.0,>=2.28.11.5",
"langchain-text-splitters",

View File

@ -479,28 +479,12 @@ class TestFactoryTypeConsistency:
class TestExtraItems:
"""Test that content blocks support extra items via __extra_items__ field."""
def test_text_block_extra_items(self) -> None:
"""Test that TextContentBlock can store extra provider-specific fields."""
block = create_text_block("Hello world")
block["openai_metadata"] = {"model": "gpt-4", "temperature": 0.7} # type: ignore[typeddict-unknown-key]
block["anthropic_usage"] = {"input_tokens": 10, "output_tokens": 20} # type: ignore[typeddict-unknown-key]
block["custom_field"] = "any value" # type: ignore[typeddict-unknown-key]
assert block["type"] == "text"
assert block["text"] == "Hello world"
assert "id" in block
assert block.get("openai_metadata") == {"model": "gpt-4", "temperature": 0.7}
assert block.get("anthropic_usage") == {"input_tokens": 10, "output_tokens": 20}
assert block.get("custom_field") == "any value"
"""Test that content blocks support extra items."""
def test_text_block_extras_field(self) -> None:
"""Test that TextContentBlock properly supports the explicit extras field."""
"""Test that TextContentBlock properly supports the extras field."""
block = create_text_block("Hello world")
# Test direct assignment to extras field
block["extras"] = {
"openai_metadata": {"model": "gpt-4", "temperature": 0.7},
"anthropic_usage": {"input_tokens": 10, "output_tokens": 20},
@ -518,32 +502,6 @@ class TestExtraItems:
assert extras.get("anthropic_usage") == expected_usage
assert extras.get("custom_field") == "any value"
def test_mixed_extra_items_types(self) -> None:
"""Test that extra items can be various types (str, int, bool, dict, list)."""
block = create_text_block("Test content")
# Add various types of extra fields
block["string_field"] = "string value" # type: ignore[typeddict-unknown-key]
block["int_field"] = 42 # type: ignore[typeddict-unknown-key]
block["float_field"] = 3.14 # type: ignore[typeddict-unknown-key]
block["bool_field"] = True # type: ignore[typeddict-unknown-key]
block["list_field"] = ["item1", "item2", "item3"] # type: ignore[typeddict-unknown-key]
block["dict_field"] = {"nested": {"deeply": "nested value"}} # type: ignore[typeddict-unknown-key]
block["none_field"] = None # type: ignore[typeddict-unknown-key]
# Verify all types are preserved
assert block.get("string_field") == "string value"
assert block.get("int_field") == 42
assert block.get("float_field") == 3.14
assert block.get("bool_field") is True
assert block.get("list_field") == ["item1", "item2", "item3"]
dict_field = block.get("dict_field", {})
assert isinstance(dict_field, dict)
nested = dict_field.get("nested", {})
assert isinstance(nested, dict)
assert nested.get("deeply") == "nested value"
assert block.get("none_field") is None
def test_extra_items_do_not_interfere_with_standard_fields(self) -> None:
"""Test that extra items don't interfere with standard field access."""
block = create_text_block("Original text", index=1)
@ -567,22 +525,21 @@ class TestExtraItems:
block = create_image_block(url="https://example.com/image.jpg")
# Add an extra field
block["status"] = "pending" # type: ignore[typeddict-unknown-key]
assert block.get("status") == "pending"
block["extras"] = {"status": "pending"}
assert block["extras"].get("status") == "pending"
# Modify the extra field
block["status"] = "processed" # type: ignore[typeddict-unknown-key]
assert block.get("status") == "processed"
block["extras"] = {"status": "processed"}
assert block["extras"].get("status") == "processed"
# Add more fields
block["metadata"] = {"version": 1} # type: ignore[typeddict-unknown-key]
metadata = block.get("metadata", {})
block["extras"] = {"metadata": {"version": 1}}
metadata = block["extras"].get("metadata", {})
assert isinstance(metadata, dict)
assert metadata.get("version") == 1
# Modify nested extra field
block["metadata"]["version"] = 2 # type: ignore[typeddict-item]
metadata = block.get("metadata", {})
metadata["version"] = 2
assert isinstance(metadata, dict)
assert metadata.get("version") == 2
@ -590,40 +547,36 @@ class TestExtraItems:
"""Test that all content block types support extra items."""
# Test each content block type
text_block = create_text_block("test")
text_block["extra"] = "text_extra" # type: ignore[typeddict-unknown-key]
assert text_block.get("extra") == "text_extra"
text_block["extras"] = {"text_extra": "a"}
assert text_block.get("extras") == {"text_extra": "a"}
image_block = create_image_block(url="https://example.com/image.jpg")
image_block["extra"] = "image_extra" # type: ignore[typeddict-unknown-key]
assert image_block.get("extra") == "image_extra"
image_block["extras"] = {"image_extra": "a"}
assert image_block.get("extras") == {"image_extra": "a"}
video_block = create_video_block(url="https://example.com/video.mp4")
video_block["extra"] = "video_extra" # type: ignore[typeddict-unknown-key]
assert video_block.get("extra") == "video_extra"
video_block["extras"] = {"video_extra": "a"}
assert video_block.get("extras") == {"video_extra": "a"}
audio_block = create_audio_block(url="https://example.com/audio.mp3")
audio_block["extra"] = "audio_extra" # type: ignore[typeddict-unknown-key]
assert audio_block.get("extra") == "audio_extra"
audio_block["extras"] = {"audio_extra": "a"}
assert audio_block.get("extras") == {"audio_extra": "a"}
file_block = create_file_block(url="https://example.com/file.pdf")
file_block["extra"] = "file_extra" # type: ignore[typeddict-unknown-key]
assert file_block.get("extra") == "file_extra"
file_block["extras"] = {"file_extra": "a"}
assert file_block.get("extras") == {"file_extra": "a"}
plain_text_block = create_plaintext_block("content")
plain_text_block["extra"] = "plaintext_extra" # type: ignore[typeddict-unknown-key]
assert plain_text_block.get("extra") == "plaintext_extra"
plain_text_block["extras"] = {"plaintext_extra": "a"}
assert plain_text_block.get("extras") == {"plaintext_extra": "a"}
tool_call = create_tool_call("tool", {"arg": "value"})
tool_call["extra"] = "tool_extra" # type: ignore[typeddict-unknown-key]
assert tool_call.get("extra") == "tool_extra"
tool_call["extras"] = {"tool_extra": "a"}
assert tool_call.get("extras") == {"tool_extra": "a"}
reasoning_block = create_reasoning_block("reasoning")
reasoning_block["extra"] = "reasoning_extra" # type: ignore[typeddict-unknown-key]
assert reasoning_block.get("extra") == "reasoning_extra"
non_standard_block = create_non_standard_block({"data": "value"})
non_standard_block["extra"] = "non_standard_extra" # type: ignore[typeddict-unknown-key]
assert non_standard_block.get("extra") == "non_standard_extra"
reasoning_block["extras"] = {"reasoning_extra": "a"}
assert reasoning_block.get("extras") == {"reasoning_extra": "a"}
class TestExtrasField:

View File

@ -81,7 +81,7 @@ def test_structured_prompt_dict() -> None:
chain = loads(dumps(prompt)) | model
assert chain.invoke({"hello": "there"}) == {"name": 1, "value": 42} # type: ignore[comparison-overlap]
assert chain.invoke({"hello": "there"}) == {"name": 1, "value": 42}
def test_structured_prompt_kwargs() -> None:
@ -104,7 +104,7 @@ def test_structured_prompt_kwargs() -> None:
assert chain.invoke({"hello": "there"}) == {"name": 1, "value": 7} # type: ignore[comparison-overlap]
assert loads(dumps(prompt)).model_dump() == prompt.model_dump()
chain = loads(dumps(prompt)) | model
assert chain.invoke({"hello": "there"}) == {"name": 1, "value": 7} # type: ignore[comparison-overlap]
assert chain.invoke({"hello": "there"}) == {"name": 1, "value": 7}
class OutputSchema(BaseModel):
name: str

File diff suppressed because it is too large Load Diff