Skip to content

style(core): fix mypy no-any-return violations#34204

Merged
Mason Daugherty (mdrxy) merged 8 commits intolangchain-ai:masterfrom
cbornet:core-no-return-any
Dec 27, 2025
Merged

style(core): fix mypy no-any-return violations#34204
Mason Daugherty (mdrxy) merged 8 commits intolangchain-ai:masterfrom
cbornet:core-no-return-any

Conversation

@cbornet
Copy link
Copy Markdown
Collaborator

@cbornet Christophe Bornet (cbornet) commented Dec 4, 2025

  • FIxed where possible
  • Used cast when not possible to fix

@github-actions github-actions bot added infra PRs made that include chores, devops, repo meta changes core `langchain-core` package issues & PRs and removed infra PRs made that include chores, devops, repo meta changes labels Dec 4, 2025
# Check if a runtime streaming flag has been passed in.
if "stream" in kwargs:
return kwargs["stream"]
return bool(kwargs["stream"])
Copy link
Copy Markdown
Collaborator Author

@cbornet Christophe Bornet (cbornet) Dec 4, 2025

Choose a reason for hiding this comment

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

calling bool() here doesn't change the behavior as _should_stream is always called in a if condition.


_SingleMessage = BaseMessage | str | dict[str, Any]
_T = TypeVar("_T", bound=_SingleMessage)
# A sequence of _SingleMessage that is NOT a bare str
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

This is needed because otherwise str matches Sequence[str]

@codspeed-hq
Copy link
Copy Markdown

codspeed-hq bot commented Dec 4, 2025

CodSpeed Performance Report

Merging #34204 will not alter performance

Comparing cbornet:core-no-return-any (370ed3f) with master (78b2d51)

⚠️ Unknown Walltime execution environment detected

Using the Walltime instrument on standard Hosted Runners will lead to inconsistent data.

For the most accurate results, we recommend using CodSpeed Macro Runners: bare-metal machines fine-tuned for performance measurement consistency.

Summary

✅ 13 untouched
⏩ 21 skipped1

Footnotes

  1. 21 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

@cbornet Christophe Bornet (cbornet) force-pushed the core-no-return-any branch 2 times, most recently from cce7d38 to ca9ecff Compare December 4, 2025 17:26
@github-actions github-actions bot added the dependencies Pull requests that update a dependency file (e.g. `pyproject.toml` or `uv.lock`) label Dec 4, 2025
@cbornet Christophe Bornet (cbornet) changed the title chore(core): fix some no-return-any mypy violations chore(core): fix mypy no-any-return rules Dec 4, 2025
@github-actions github-actions bot added the infra PRs made that include chores, devops, repo meta changes label Dec 4, 2025
@cbornet Christophe Bornet (cbornet) changed the title chore(core): fix mypy no-any-return rules chore(core): fix mypy no-any-return violations Dec 4, 2025
@github-actions github-actions bot added infra PRs made that include chores, devops, repo meta changes and removed infra PRs made that include chores, devops, repo meta changes labels Dec 4, 2025

prompt = ChatPromptTemplate(messages=[self])
return prompt + other
return prompt.__add__(other)
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

mypy doesn't figure the + operator...

try:
while True:
chunk = await coro_with_context(py_anext(iterator), context)
chunk = await coro_with_context(anext(iterator), context)
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

we now use python 3.10+ so py_anext is not needed anymore

@cbornet Christophe Bornet (cbornet) force-pushed the core-no-return-any branch 2 times, most recently from 31cf767 to 1bac6c8 Compare December 9, 2025 16:56
@mdrxy Mason Daugherty (mdrxy) changed the title chore(core): fix mypy no-any-return violations style(core): fix mypy no-any-return violations Dec 20, 2025
@github-actions github-actions bot added linting and removed infra PRs made that include chores, devops, repo meta changes labels Dec 20, 2025
Args:
coro: The coroutine to await.
context: The context to use.
create_task: Whether to create a task.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Removing create_task is technically breaking (even though it wasn't exposed in __init__.py)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Let's add a deprecation warning if create_task=True is passed.



def get_from_dict_or_env(
data: dict[str, Any],
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think values is dict[str, Any] that can contain floats, objects, etc., not dict[str, str].

Copy link
Copy Markdown
Collaborator Author

@cbornet Christophe Bornet (cbornet) Dec 22, 2025

Choose a reason for hiding this comment

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

From its signature get_from_dict_or_env must return str and we return the value of the data dict.
So data should be dict[str, str] or else get_from_dict_or_env should be changed to return Any.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Changing to dict[str, str] would be a breaking change. I understand the type inconsistency concern. If we want stricter correctness, we could add a runtime check, but I'd lean toward keeping dict[str, Any] for compatibility for now.

Copy link
Copy Markdown
Collaborator Author

@cbornet Christophe Bornet (cbornet) Dec 26, 2025

Choose a reason for hiding this comment

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

I changed to call str(data[key]) so the signature doesn't break. Can you take a look again ?


def _parse_json(
json_str: str, *, parser: Callable[[str], Any] = parse_partial_json
json_str: str, *, parser: Callable[[str], dict[str, Any]] = parse_partial_json
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

parse_partial_json is typed to return Any, not dict[str, Any]

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Yes but mypy misses it and doesn't complain, so there's no need to cast.

Copy link
Copy Markdown
Collaborator Author

@cbornet Christophe Bornet (cbornet) Dec 22, 2025

Choose a reason for hiding this comment

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

The other possibility would be to make _parse_json/parse_json_markdown return Any which would be closer to the truth. And have callers cast or check the type the result. There are callers of parse_json_markdown in core, langchain-classic and community.
It seems some callers already expect parse_json_markdown to return something different than a dict and have isinstance checks (eg. JSONAgentOutputParser.parse)
LMK what you prefer.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Mypy not catching something doesn't make the types correct. I think making both return Any is a more honest approach.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

done

@mdrxy
Copy link
Copy Markdown
Member

^outside of the above, looks good!

@cbornet Christophe Bornet (cbornet) force-pushed the core-no-return-any branch 2 times, most recently from 6957e92 to 597ad79 Compare December 22, 2025 10:35
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR addresses mypy's no-any-return violations by fixing type annotations and adding explicit casts where necessary. The main goal is to enable stricter mypy checking by removing the warn_return_any = false configuration setting.

Key Changes:

  • Added explicit type parameters to generic classes (e.g., RunnableLambda[int, int])
  • Added cast statements to satisfy mypy where type inference fails
  • Fixed return types and improved type annotations throughout the codebase

Reviewed changes

Copilot reviewed 41 out of 41 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
libs/core/pyproject.toml Removed warn_return_any = false to enable stricter type checking
libs/core/tests/unit_tests/test_tools.py Fixed test functions to return properly typed values and convert integers to strings
libs/core/tests/unit_tests/runnables/test_utils.py Added type parameters to RunnableLambda instances and fixed return type casting
libs/core/tests/unit_tests/runnables/test_tracing_interops.py Added type parameters to RunnableLambda and added explicit casting for dictionary access
libs/core/tests/unit_tests/runnables/test_runnable_events_v2.py Added type parameters to RunnableLambda instances
libs/core/tests/unit_tests/runnables/test_runnable_events_v1.py Added type parameters to RunnableLambda instances
libs/core/tests/unit_tests/runnables/test_runnable.py Added explicit integer casting for kwargs values
libs/core/tests/unit_tests/runnables/test_history.py Fixed assertion statement that was incorrectly chained and updated type hint
libs/core/tests/unit_tests/pydantic_utils.py Replaced is_basemodel_subclass with direct issubclass checks
libs/core/tests/unit_tests/prompts/test_structured.py Replaced is_basemodel_subclass with direct issubclass check
libs/core/tests/unit_tests/messages/test_utils.py Reorganized type ignore comment placement
libs/core/langchain_core/vectorstores/utils.py Added cast for numpy array return value
libs/core/langchain_core/utils/*.py Added casts and improved type annotations across utility modules
libs/core/langchain_core/tracers/*.py Added casts for dictionary access and improved return type annotations
libs/core/langchain_core/tools/*.py Added casts for Pydantic model creation and schema access
libs/core/langchain_core/runnables/*.py Added type parameters and improved type annotations
libs/core/langchain_core/prompts/*.py Added casts, improved type annotations, and added abstract method declaration
libs/core/langchain_core/output_parsers/*.py Added overloads and improved return type handling
libs/core/langchain_core/messages/*.py Enhanced type checking with runtime validation and added casts
libs/core/langchain_core/language_models/*.py Added casts for serialization and improved type annotations
libs/core/langchain_core/_api/internal.py Added cast for module name access
Comments suppressed due to low confidence (1)

libs/core/tests/unit_tests/runnables/test_history.py:1

  • The assertion on line 100 is now separate from the list comprehension on line 98, but output will be a list containing the boolean result of the comparison that was previously on line 99. The code should assign the result of the list comprehension to output before asserting. Remove the comparison from inside the list comprehension and keep it only in the assert statement.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

return StringPromptValue(text=await self.aformat(**kwargs))

@override
@abstractmethod
Copy link

Copilot AI Dec 26, 2025

Choose a reason for hiding this comment

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

Adding an abstract method declaration to a base class that likely already has concrete implementations in subclasses could break existing code. If format is already implemented in this class elsewhere (not visible in the diff), this abstract declaration is incorrect. Verify that this method is not already implemented in StringPromptTemplate or ensure all subclasses properly override it.

Suggested change
@abstractmethod

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Hi Copilot, you missed that this is an override of the parent method which is already abstract so it doesn't break anything 😉.

@github-actions github-actions bot added the langchain-classic `langchain-classic` package issues & PRs label Dec 26, 2025
Copy link
Copy Markdown
Member

@mdrxy Mason Daugherty (mdrxy) left a comment

Choose a reason for hiding this comment

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

TY!

@mdrxy Mason Daugherty (mdrxy) merged commit a92c032 into langchain-ai:master Dec 27, 2025
89 checks passed
@cbornet Christophe Bornet (cbornet) deleted the core-no-return-any branch December 27, 2025 09:10
DevByteAI (devbyteai) pushed a commit to devbyteai/langchain that referenced this pull request Jan 21, 2026
* FIxed where possible
* Used `cast` when not possible to fix

---------

Co-authored-by: Mason Daugherty <github@mdrxy.com>
Co-authored-by: Mason Daugherty <mason@langchain.dev>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

core `langchain-core` package issues & PRs dependencies Pull requests that update a dependency file (e.g. `pyproject.toml` or `uv.lock`) external langchain-classic `langchain-classic` package issues & PRs linting

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants