From f6d4fec4d588d3bcf7f501405f74f4e053364bd4 Mon Sep 17 00:00:00 2001 From: Christophe Bornet Date: Sat, 22 Feb 2025 23:46:28 +0100 Subject: [PATCH] core: Add ruff rules ANN (type annotations) (#29271) See https://docs.astral.sh/ruff/rules/#flake8-annotations-ann The interest compared to only mypy is that ruff is very fast at detecting missing annotations. ANN101 and ANN102 are deprecated so we ignore them ANN401 (no Any type) ignored to be in sync with mypy config --------- Co-authored-by: ccurme --- .../langchain_core/_api/beta_decorator.py | 18 +++++++++---- libs/core/langchain_core/_api/deprecation.py | 26 ++++++++++++++----- libs/core/langchain_core/runnables/base.py | 4 +-- libs/core/langchain_core/utils/utils.py | 12 ++++----- libs/core/pyproject.toml | 6 +++-- .../unit_tests/runnables/test_runnable.py | 6 ++--- .../runnables/test_runnable_events_v2.py | 5 ++-- .../runnables/test_tracing_interops.py | 2 +- libs/core/tests/unit_tests/test_tools.py | 2 +- 9 files changed, 52 insertions(+), 29 deletions(-) diff --git a/libs/core/langchain_core/_api/beta_decorator.py b/libs/core/langchain_core/_api/beta_decorator.py index e844a60fb5f..b1415263323 100644 --- a/libs/core/langchain_core/_api/beta_decorator.py +++ b/libs/core/langchain_core/_api/beta_decorator.py @@ -156,28 +156,36 @@ def beta( class _BetaProperty(property): """A beta property.""" - def __init__(self, fget=None, fset=None, fdel=None, doc=None): + def __init__( + self, + fget: Union[Callable[[Any], Any], None] = None, + fset: Union[Callable[[Any, Any], None], None] = None, + fdel: Union[Callable[[Any], None], None] = None, + doc: Union[str, None] = None, + ) -> None: super().__init__(fget, fset, fdel, doc) self.__orig_fget = fget self.__orig_fset = fset self.__orig_fdel = fdel - def __get__(self, instance, owner=None): + def __get__( + self, instance: Any, owner: Union[type, None] = None + ) -> Any: if instance is not None or owner is not None: emit_warning() return self.fget(instance) - def __set__(self, instance, value): + def __set__(self, instance: Any, value: Any) -> None: if instance is not None: emit_warning() return self.fset(instance, value) - def __delete__(self, instance): + def __delete__(self, instance: Any) -> None: if instance is not None: emit_warning() return self.fdel(instance) - def __set_name__(self, owner, set_name): + def __set_name__(self, owner: Union[type, None], set_name: str) -> None: nonlocal _name if _name == "": _name = set_name diff --git a/libs/core/langchain_core/_api/deprecation.py b/libs/core/langchain_core/_api/deprecation.py index a2cccaa2be3..ad3d806b0da 100644 --- a/libs/core/langchain_core/_api/deprecation.py +++ b/libs/core/langchain_core/_api/deprecation.py @@ -270,28 +270,40 @@ def deprecated( class _DeprecatedProperty(property): """A deprecated property.""" - def __init__(self, fget=None, fset=None, fdel=None, doc=None): # type: ignore[no-untyped-def] + def __init__( + self, + fget: Union[Callable[[Any], Any], None] = None, + fset: Union[Callable[[Any, Any], None], None] = None, + fdel: Union[Callable[[Any], None], None] = None, + doc: Union[str, None] = None, + ) -> None: super().__init__(fget, fset, fdel, doc) self.__orig_fget = fget self.__orig_fset = fset self.__orig_fdel = fdel - def __get__(self, instance, owner=None): # type: ignore[no-untyped-def] + def __get__( + self, instance: Any, owner: Union[type, None] = None + ) -> Any: if instance is not None or owner is not None: emit_warning() + if self.fget is None: + return None return self.fget(instance) - def __set__(self, instance, value): # type: ignore[no-untyped-def] + def __set__(self, instance: Any, value: Any) -> None: if instance is not None: emit_warning() - return self.fset(instance, value) + if self.fset is not None: + self.fset(instance, value) - def __delete__(self, instance): # type: ignore[no-untyped-def] + def __delete__(self, instance: Any) -> None: if instance is not None: emit_warning() - return self.fdel(instance) + if self.fdel is not None: + self.fdel(instance) - def __set_name__(self, owner, set_name): # type: ignore[no-untyped-def] + def __set_name__(self, owner: Union[type, None], set_name: str) -> None: nonlocal _name if _name == "": _name = set_name diff --git a/libs/core/langchain_core/runnables/base.py b/libs/core/langchain_core/runnables/base.py index f34596c5ad0..1840e3dbe7b 100644 --- a/libs/core/langchain_core/runnables/base.py +++ b/libs/core/langchain_core/runnables/base.py @@ -4641,7 +4641,7 @@ class RunnableLambda(Runnable[Input, Output]): ) @wraps(func) - async def f(*args, **kwargs): # type: ignore[no-untyped-def] + async def f(*args: Any, **kwargs: Any) -> Any: return await run_in_executor(config, func, *args, **kwargs) afunc = f @@ -4889,7 +4889,7 @@ class RunnableLambda(Runnable[Input, Output]): ) @wraps(func) - async def f(*args, **kwargs): # type: ignore[no-untyped-def] + async def f(*args: Any, **kwargs: Any) -> Any: return await run_in_executor(config, func, *args, **kwargs) afunc = f diff --git a/libs/core/langchain_core/utils/utils.py b/libs/core/langchain_core/utils/utils.py index 6cfbc9ceaf8..17114268ede 100644 --- a/libs/core/langchain_core/utils/utils.py +++ b/libs/core/langchain_core/utils/utils.py @@ -6,7 +6,7 @@ import functools import importlib import os import warnings -from collections.abc import Sequence +from collections.abc import Iterator, Sequence from importlib.metadata import version from typing import Any, Callable, Optional, Union, overload @@ -73,7 +73,7 @@ def raise_for_status_with_text(response: Response) -> None: @contextlib.contextmanager -def mock_now(dt_value): # type: ignore +def mock_now(dt_value: datetime.datetime) -> Iterator[type]: """Context manager for mocking out datetime.now() in unit tests. Args: @@ -91,9 +91,9 @@ def mock_now(dt_value): # type: ignore """Mock datetime.datetime.now() with a fixed datetime.""" @classmethod - def now(cls): # type: ignore + def now(cls, tz: Union[datetime.tzinfo, None] = None) -> "MockDateTime": # Create a copy of dt_value. - return datetime.datetime( + return MockDateTime( dt_value.year, dt_value.month, dt_value.day, @@ -105,11 +105,11 @@ def mock_now(dt_value): # type: ignore ) real_datetime = datetime.datetime - datetime.datetime = MockDateTime + datetime.datetime = MockDateTime # type: ignore[misc] try: yield datetime.datetime finally: - datetime.datetime = real_datetime + datetime.datetime = real_datetime # type: ignore[misc] def guard_import( diff --git a/libs/core/pyproject.toml b/libs/core/pyproject.toml index a2df49f871e..75aa6bbe055 100644 --- a/libs/core/pyproject.toml +++ b/libs/core/pyproject.toml @@ -77,8 +77,10 @@ target-version = "py39" [tool.ruff.lint] -select = [ "ASYNC", "B", "C4", "COM", "DJ", "E", "EM", "EXE", "F", "FLY", "FURB", "I", "ICN", "INT", "LOG", "N", "NPY", "PD", "PIE", "Q", "RSE", "S", "SIM", "SLOT", "T10", "T201", "TID", "TRY", "UP", "W", "YTT",] -ignore = [ "COM812", "UP007", "S110", "S112",] +select = [ "ANN", "ASYNC", "B", "C4", "COM", "DJ", "E", "EM", "EXE", "F", "FLY", "FURB", "I", "ICN", "INT", "LOG", "N", "NPY", "PD", "PIE", "Q", "RSE", "S", "SIM", "SLOT", "T10", "T201", "TID", "TRY", "UP", "W", "YTT",] +ignore = [ "ANN401", "COM812", "UP007", "S110", "S112",] +flake8-annotations.allow-star-arg-any = true +flake8-annotations.mypy-init-return = true [tool.coverage.run] omit = [ "tests/*",] diff --git a/libs/core/tests/unit_tests/runnables/test_runnable.py b/libs/core/tests/unit_tests/runnables/test_runnable.py index 01cd8a026c5..dd28080ea2c 100644 --- a/libs/core/tests/unit_tests/runnables/test_runnable.py +++ b/libs/core/tests/unit_tests/runnables/test_runnable.py @@ -567,7 +567,7 @@ def test_lambda_schemas(snapshot: SnapshotAssertion) -> None: "required": ["bye", "hello"], } - def get_value(input): # type: ignore[no-untyped-def] + def get_value(input): # type: ignore[no-untyped-def] # noqa: ANN001,ANN202 return input["variable_name"] assert RunnableLambda(get_value).get_input_jsonschema() == { @@ -577,7 +577,7 @@ def test_lambda_schemas(snapshot: SnapshotAssertion) -> None: "required": ["variable_name"], } - async def aget_value(input): # type: ignore[no-untyped-def] + async def aget_value(input): # type: ignore[no-untyped-def] # noqa: ANN001,ANN202 return (input["variable_name"], input.get("another")) assert RunnableLambda(aget_value).get_input_jsonschema() == { @@ -590,7 +590,7 @@ def test_lambda_schemas(snapshot: SnapshotAssertion) -> None: "required": ["another", "variable_name"], } - async def aget_values(input): # type: ignore[no-untyped-def] + async def aget_values(input): # type: ignore[no-untyped-def] # noqa: ANN001,ANN202 return { "hello": input["variable_name"], "bye": input["variable_name"], diff --git a/libs/core/tests/unit_tests/runnables/test_runnable_events_v2.py b/libs/core/tests/unit_tests/runnables/test_runnable_events_v2.py index 578eee5fa6c..e1e1f37b904 100644 --- a/libs/core/tests/unit_tests/runnables/test_runnable_events_v2.py +++ b/libs/core/tests/unit_tests/runnables/test_runnable_events_v2.py @@ -9,6 +9,7 @@ from functools import partial from itertools import cycle from typing import ( Any, + Callable, Optional, cast, ) @@ -1902,10 +1903,10 @@ async def test_runnable_with_message_history() -> None: # so we can raise them in this main thread raised_errors = [] - def collect_errors(fn): # type: ignore + def collect_errors(fn: Callable[..., Any]) -> Callable[..., Any]: nonlocal raised_errors - def _get_output_messages(*args, **kwargs): # type: ignore + def _get_output_messages(*args: Any, **kwargs: Any) -> Any: try: return fn(*args, **kwargs) except Exception as e: diff --git a/libs/core/tests/unit_tests/runnables/test_tracing_interops.py b/libs/core/tests/unit_tests/runnables/test_tracing_interops.py index 77922a5616c..8c08f0f5c11 100644 --- a/libs/core/tests/unit_tests/runnables/test_tracing_interops.py +++ b/libs/core/tests/unit_tests/runnables/test_tracing_interops.py @@ -465,7 +465,7 @@ def test_tree_is_constructed(parent_type: Literal["ls", "lc"]) -> None: else: @RunnableLambda - def parent(_) -> str: # type: ignore + def parent(_: Any) -> str: return child.invoke("foo") tracer = LangChainTracer() diff --git a/libs/core/tests/unit_tests/test_tools.py b/libs/core/tests/unit_tests/test_tools.py index 06c82946ebe..bc32aa4895e 100644 --- a/libs/core/tests/unit_tests/test_tools.py +++ b/libs/core/tests/unit_tests/test_tools.py @@ -2217,7 +2217,7 @@ def test_tool_args_schema_pydantic_v2_with_metadata() -> None: ) @tool(args_schema=Foo) - def foo(x): # type: ignore[no-untyped-def] + def foo(x) -> list[int]: # type: ignore[no-untyped-def] # noqa: ANN001 """Foo.""" return x