ref: Some ruff fixes from preview (#5420)

* Some ruff fixes from preview

* [autofix.ci] apply automated fixes

* [autofix.ci] apply automated fixes (attempt 2/3)

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
This commit is contained in:
Christophe Bornet 2024-12-28 22:25:35 +01:00 • committed by GitHub
commit e91bcc2520
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
79 changed files with 402 additions and 374 deletions

View file

@ -152,7 +152,7 @@ dev-dependencies = [
"types-redis>=4.6.0.5", "types-redis>=4.6.0.5",
"ipykernel>=6.29.0", "ipykernel>=6.29.0",
"mypy>=1.11.0", "mypy>=1.11.0",
"ruff>=0.8.2,<0.9.0", "ruff>=0.8.4,<0.9.0",
"httpx>=0.27.0", "httpx>=0.27.0",
"pytest>=8.2.0", "pytest>=8.2.0",
"types-requests>=2.32.0", "types-requests>=2.32.0",

View file

@ -90,7 +90,7 @@ class AgentQL(Component):
except httpx.HTTPStatusError as e: except httpx.HTTPStatusError as e:
response = e.response response = e.response
if response.status_code in [401, 403]: if response.status_code in {401, 403}:
self.status = "Please, provide a valid API Key. You can create one at https://dev.agentql.com." self.status = "Please, provide a valid API Key. You can create one at https://dev.agentql.com."
else: else:
try: try:

View file

@ -71,7 +71,7 @@ class DirectoryComponent(Component):
def load_directory(self) -> list[Data]: def load_directory(self) -> list[Data]:
path = self.path path = self.path
types = self.types if self.types else TEXT_FILE_TYPES types = self.types or TEXT_FILE_TYPES
depth = self.depth depth = self.depth
max_concurrency = self.max_concurrency max_concurrency = self.max_concurrency
load_hidden = self.load_hidden load_hidden = self.load_hidden

View file

@ -131,12 +131,12 @@ class LangWatchComponent(Component):
# Clear component's dynamic attributes # Clear component's dynamic attributes
for attr in list(self.__dict__.keys()): for attr in list(self.__dict__.keys()):
if attr not in default_keys and attr not in [ if attr not in default_keys and attr not in {
"evaluators", "evaluators",
"dynamic_inputs", "dynamic_inputs",
"_code", "_code",
"current_evaluator", "current_evaluator",
]: }:
delattr(self, attr) delattr(self, attr)
# Add new dynamic inputs # Add new dynamic inputs
@ -177,7 +177,7 @@ class LangWatchComponent(Component):
input_fields = [ input_fields = [
field field
for field in evaluator.get("requiredFields", []) + evaluator.get("optionalFields", []) for field in evaluator.get("requiredFields", []) + evaluator.get("optionalFields", [])
if field not in ["input", "output"] if field not in {"input", "output"}
] ]
for field in input_fields: for field in input_fields:

View file

@ -126,8 +126,7 @@ class ConditionalRouterComponent(Component):
def update_build_config(self, build_config: dict, field_value: str, field_name: str | None = None) -> dict: def update_build_config(self, build_config: dict, field_value: str, field_name: str | None = None) -> dict:
if field_name == "operator": if field_name == "operator":
if field_value == "matches regex": if field_value == "matches regex":
if "case_sensitive" in build_config: build_config.pop("case_sensitive", None)
del build_config["case_sensitive"]
# Ensure case_sensitive is present for all other operators # Ensure case_sensitive is present for all other operators
elif "case_sensitive" not in build_config: elif "case_sensitive" not in build_config:
case_sensitive_input = next( case_sensitive_input = next(

View file

@ -22,7 +22,7 @@ class OpenRouterComponent(LCModelComponent):
display_name = "OpenRouter" display_name = "OpenRouter"
description = ( description = (
"OpenRouter provides unified access to multiple AI models " "from different providers through a single API." "OpenRouter provides unified access to multiple AI models from different providers through a single API."
) )
icon = "OpenRouter" icon = "OpenRouter"
@ -180,9 +180,7 @@ class OpenRouterComponent(LCModelComponent):
build_config["model_name"]["value"] = models[0]["id"] build_config["model_name"]["value"] = models[0]["id"]
tooltips = { tooltips = {
model["id"]: ( model["id"]: (f"{model['name']}\nContext Length: {model['context_length']}\n{model['description']}")
f"{model['name']}\n" f"Context Length: {model['context_length']}\n" f"{model['description']}"
)
for model in models for model in models
} }
build_config["model_name"]["tooltips"] = tooltips build_config["model_name"]["tooltips"] = tooltips

View file

@ -10,7 +10,7 @@ from langflow.utils.constants import MESSAGE_SENDER_AI
class NeedleComponent(Component): class NeedleComponent(Component):
display_name = "Needle Retriever" display_name = "Needle Retriever"
description = "A retriever that uses the Needle API to search collections " "and generates responses using OpenAI." description = "A retriever that uses the Needle API to search collections and generates responses using OpenAI."
documentation = "https://docs.needle-ai.com" documentation = "https://docs.needle-ai.com"
icon = "search" icon = "search"
name = "needle" name = "needle"
@ -105,8 +105,8 @@ class NeedleComponent(Component):
if str(output_type).lower().strip() == "chunks": if str(output_type).lower().strip() == "chunks":
# If chunks selected, include full context and answer # If chunks selected, include full context and answer
docs = result["source_documents"] docs = result["source_documents"]
context = "\n\n".join([f"Document {i+1}:\n{doc.page_content}" for i, doc in enumerate(docs)]) context = "\n\n".join([f"Document {i + 1}:\n{doc.page_content}" for i, doc in enumerate(docs)])
text_content = f"Question: {query}\n\n" f"Context:\n{context}\n\n" f"Answer: {result['answer']}" text_content = f"Question: {query}\n\nContext:\n{context}\n\nAnswer: {result['answer']}"
else: else:
# If answer selected, only include the answer # If answer selected, only include the answer
text_content = result["answer"] text_content = result["answer"]

View file

@ -144,7 +144,7 @@ class DataFrameOperationsComponent(Component):
build_config["new_column_value"]["show"] = True build_config["new_column_value"]["show"] = True
elif field_value == "Select Columns": elif field_value == "Select Columns":
build_config["columns_to_select"]["show"] = True build_config["columns_to_select"]["show"] = True
elif field_value in ["Head", "Tail"]: elif field_value in {"Head", "Tail"}:
build_config["num_rows"]["show"] = True build_config["num_rows"]["show"] = True
elif field_value == "Replace Value": elif field_value == "Replace Value":
build_config["column_name"]["show"] = True build_config["column_name"]["show"] = True

View file

@ -74,7 +74,7 @@ class MergeDataComponent(Component):
for key, value in data_input.data.items(): for key, value in data_input.data.items():
if key in result_data and isinstance(value, str): if key in result_data and isinstance(value, str):
if isinstance(result_data[key], list): if isinstance(result_data[key], list):
cast(list[str], result_data[key]).append(value) cast("list[str]", result_data[key]).append(value)
else: else:
result_data[key] = [result_data[key], value] result_data[key] = [result_data[key], value]
else: else:

View file

@ -295,7 +295,7 @@ class AstraDBVectorStoreComponent(LCVectorStoreComponent):
def update_build_config(self, build_config: dict, field_value: str, field_name: str | None = None): def update_build_config(self, build_config: dict, field_value: str, field_name: str | None = None):
# Always attempt to update the database list # Always attempt to update the database list
if field_name in ["token", "api_endpoint", "collection_name"]: if field_name in {"token", "api_endpoint", "collection_name"}:
# Update the database selector # Update the database selector
build_config["api_endpoint"]["options"] = self._initialize_database_options() build_config["api_endpoint"]["options"] = self._initialize_database_options()

View file

@ -1101,7 +1101,8 @@ class Graph:
return False return False
return self.vertex_edges_are_identical(vertex, other_vertex) return self.vertex_edges_are_identical(vertex, other_vertex)
def vertex_edges_are_identical(self, vertex: Vertex, other_vertex: Vertex) -> bool: @staticmethod
def vertex_edges_are_identical(vertex: Vertex, other_vertex: Vertex) -> bool:
same_length = len(vertex.edges) == len(other_vertex.edges) same_length = len(vertex.edges) == len(other_vertex.edges)
if not same_length: if not same_length:
return False return False
@ -1747,7 +1748,8 @@ class Graph:
new_edge = Edge(source, target, edge) new_edge = Edge(source, target, edge)
return new_edge return new_edge
def _get_vertex_class(self, node_type: str, node_base_type: str, node_id: str) -> type[Vertex]: @staticmethod
def _get_vertex_class(node_type: str, node_base_type: str, node_id: str) -> type[Vertex]:
"""Returns the node class based on the node type.""" """Returns the node class based on the node type."""
# First we check for the node_base_type # First we check for the node_base_type
node_name = node_id.split("-")[0] node_name = node_id.split("-")[0]
@ -1830,7 +1832,8 @@ class Graph:
self._record_snapshot() self._record_snapshot()
return self return self
def get_children_by_vertex_type(self, vertex: Vertex, vertex_type: str) -> list[Vertex]: @staticmethod
def get_children_by_vertex_type(vertex: Vertex, vertex_type: str) -> list[Vertex]:
"""Returns the children of a vertex based on the vertex type.""" """Returns the children of a vertex based on the vertex type."""
children = [] children = []
vertex_types = [vertex.data["type"]] vertex_types = [vertex.data["type"]]
@ -2059,7 +2062,8 @@ class Graph:
self._first_layer = first_layer self._first_layer = first_layer
return first_layer return first_layer
def sort_interface_components_first(self, vertices_layers: list[list[str]]) -> list[list[str]]: @staticmethod
def sort_interface_components_first(vertices_layers: list[list[str]]) -> list[list[str]]:
"""Sorts the vertices in the graph so that vertices containing ChatInput or ChatOutput come first.""" """Sorts the vertices in the graph so that vertices containing ChatInput or ChatOutput come first."""
def contains_interface_component(vertex): def contains_interface_component(vertex):
@ -2097,9 +2101,6 @@ class Graph:
This method is responsible for building the run map for the graph, This method is responsible for building the run map for the graph,
which maps each node in the graph to its corresponding run function. which maps each node in the graph to its corresponding run function.
Returns:
None
""" """
self.run_manager.build_run_map(predecessor_map=self.predecessor_map, vertices_to_run=self.vertices_to_run) self.run_manager.build_run_map(predecessor_map=self.predecessor_map, vertices_to_run=self.vertices_to_run)
@ -2169,7 +2170,8 @@ class Graph:
in_degree[vertex.id] = 0 in_degree[vertex.id] = 0
return in_degree return in_degree
def build_adjacency_maps(self, edges: list[CycleEdge]) -> tuple[dict[str, list[str]], dict[str, list[str]]]: @staticmethod
def build_adjacency_maps(edges: list[CycleEdge]) -> tuple[dict[str, list[str]], dict[str, list[str]]]:
"""Returns the adjacency maps for the graph.""" """Returns the adjacency maps for the graph."""
predecessor_map: dict[str, list[str]] = defaultdict(list) predecessor_map: dict[str, list[str]] = defaultdict(list)
successor_map: dict[str, list[str]] = defaultdict(list) successor_map: dict[str, list[str]] = defaultdict(list)

View file

@ -110,9 +110,6 @@ def update_template(template, g_nodes) -> None:
Args: Args:
template (dict): The new template to update the node with. template (dict): The new template to update the node with.
g_nodes (list): The list of nodes in the graph. g_nodes (list): The list of nodes in the graph.
Returns:
None
""" """
for value in template.values(): for value in template.values():
if not value.get("proxy"): if not value.get("proxy"):
@ -161,9 +158,6 @@ def set_new_target_handle(proxy_id, new_edge, target_handle, node) -> None:
new_edge (dict): The new edge to be created. new_edge (dict): The new edge to be created.
target_handle (dict): The target handle of the edge. target_handle (dict): The target handle of the edge.
node (dict): The node containing the edge. node (dict): The node containing the edge.
Returns:
None
""" """
new_edge["target"] = proxy_id new_edge["target"] = proxy_id
type_ = target_handle.get("type") type_ = target_handle.get("type")

View file

@ -423,7 +423,7 @@ class Vertex:
else: else:
msg = f"Invalid value type {type(val)} for field {field_name}" msg = f"Invalid value type {type(val)} for field {field_name}"
raise ValueError(msg) raise ValueError(msg)
elif val is not None and val != "": elif val:
params[field_name] = val params[field_name] = val
if field.get("load_from_db"): if field.get("load_from_db"):
@ -596,7 +596,8 @@ class Vertex:
result = await value.get_result(self, target_handle_name=key) result = await value.get_result(self, target_handle_name=key)
self.params[key][sub_key] = result self.params[key][sub_key] = result
def _is_vertex(self, value): @staticmethod
def _is_vertex(value):
"""Checks if the provided value is an instance of Vertex.""" """Checks if the provided value is an instance of Vertex."""
return isinstance(value, Vertex) return isinstance(value, Vertex)

File diff suppressed because one or more lines are too long

View file

@ -1,3 +1,5 @@
from typing_extensions import override
from langflow.services.deps import get_settings_service from langflow.services.deps import get_settings_service
from langflow.utils.lazy_load import LazyLoadDictBase from langflow.utils.lazy_load import LazyLoadDictBase
@ -13,6 +15,7 @@ class AllTypesDict(LazyLoadDictBase):
"Custom": ["Custom Tool", "Python Function"], "Custom": ["Custom Tool", "Python Function"],
} }
@override
def get_type_dict(self): def get_type_dict(self):
from langflow.interface.types import get_all_types_dict from langflow.interface.types import get_all_types_dict

View file

@ -15,7 +15,7 @@ from loguru._file_sink import FileSink
from loguru._simple_sinks import AsyncSink from loguru._simple_sinks import AsyncSink
from platformdirs import user_cache_dir from platformdirs import user_cache_dir
from rich.logging import RichHandler from rich.logging import RichHandler
from typing_extensions import NotRequired from typing_extensions import NotRequired, override
from langflow.settings import DEV from langflow.settings import DEV
@ -285,6 +285,7 @@ class InterceptHandler(logging.Handler):
See https://loguru.readthedocs.io/en/stable/overview.html#entirely-compatible-with-standard-logging. See https://loguru.readthedocs.io/en/stable/overview.html#entirely-compatible-with-standard-logging.
""" """
@override
def emit(self, record) -> None: def emit(self, record) -> None:
# Get corresponding Loguru level if it exists # Get corresponding Loguru level if it exists
try: try:

View file

@ -19,7 +19,7 @@ from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor
from pydantic import PydanticDeprecatedSince20 from pydantic import PydanticDeprecatedSince20
from pydantic_core import PydanticSerializationError from pydantic_core import PydanticSerializationError
from rich import print as rprint from rich import print as rprint
from starlette.middleware.base import BaseHTTPMiddleware from starlette.middleware.base import BaseHTTPMiddleware, RequestResponseEndpoint
from langflow.api import health_check_router, log_router, router from langflow.api import health_check_router, log_router, router
from langflow.initial_setup.setup import ( from langflow.initial_setup.setup import (
@ -45,7 +45,7 @@ class RequestCancelledMiddleware(BaseHTTPMiddleware):
def __init__(self, app) -> None: def __init__(self, app) -> None:
super().__init__(app) super().__init__(app)
async def dispatch(self, request: Request, call_next): async def dispatch(self, request: Request, call_next: RequestResponseEndpoint) -> Response:
sentinel = object() sentinel = object()
async def cancel_handler(): async def cancel_handler():
@ -68,7 +68,7 @@ class RequestCancelledMiddleware(BaseHTTPMiddleware):
class JavaScriptMIMETypeMiddleware(BaseHTTPMiddleware): class JavaScriptMIMETypeMiddleware(BaseHTTPMiddleware):
async def dispatch(self, request: Request, call_next): async def dispatch(self, request: Request, call_next: RequestResponseEndpoint) -> Response:
try: try:
response = await call_next(request) response = await call_next(request)
except Exception as exc: except Exception as exc:

View file

@ -26,7 +26,8 @@ class ContentSizeLimitMiddleware:
self.app = app self.app = app
self.logger = logger self.logger = logger
def receive_wrapper(self, receive): @staticmethod
def receive_wrapper(receive):
received = 0 received = 0
async def inner(): async def inner():

View file

@ -32,7 +32,7 @@ class DataFrame(pandas_DataFrame):
>>> dataset = DataFrame({"name": ["John", "Jane"], "age": [30, 25]}) >>> dataset = DataFrame({"name": ["John", "Jane"], "age": [30, 25]})
""" """
def __init__(self, data: None | list[dict | Data] | dict | pd.DataFrame = None, **kwargs): def __init__(self, data: list[dict | Data] | dict | pd.DataFrame | None = None, **kwargs):
if data is None: if data is None:
super().__init__(**kwargs) super().__init__(**kwargs)
return return

View file

@ -6,7 +6,7 @@ from typing_extensions import Protocol
from langflow.schema.message import ContentBlock, Message from langflow.schema.message import ContentBlock, Message
from langflow.schema.playground_events import PlaygroundEvent from langflow.schema.playground_events import PlaygroundEvent
LoggableType: TypeAlias = str | dict | list | int | float | bool | None | BaseModel | PlaygroundEvent LoggableType: TypeAlias = str | dict | list | int | float | bool | BaseModel | PlaygroundEvent | None
class LogFunctionType(Protocol): class LogFunctionType(Protocol):

View file

@ -1,3 +1,5 @@
from typing_extensions import override
from langflow.services.auth.service import AuthService from langflow.services.auth.service import AuthService
from langflow.services.factory import ServiceFactory from langflow.services.factory import ServiceFactory
@ -8,5 +10,6 @@ class AuthServiceFactory(ServiceFactory):
def __init__(self) -> None: def __init__(self) -> None:
super().__init__(AuthService) super().__init__(AuthService)
@override
def create(self, settings_service): def create(self, settings_service):
return AuthService(settings_service) return AuthService(settings_service)

View file

@ -2,6 +2,8 @@ from __future__ import annotations
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from typing_extensions import override
from langflow.logging.logger import logger from langflow.logging.logger import logger
from langflow.services.cache.disk import AsyncDiskCache from langflow.services.cache.disk import AsyncDiskCache
from langflow.services.cache.service import AsyncInMemoryCache, CacheService, RedisCache, ThreadingInMemoryCache from langflow.services.cache.service import AsyncInMemoryCache, CacheService, RedisCache, ThreadingInMemoryCache
@ -15,6 +17,7 @@ class CacheServiceFactory(ServiceFactory):
def __init__(self) -> None: def __init__(self) -> None:
super().__init__(CacheService) super().__init__(CacheService)
@override
def create(self, settings_service: SettingsService): def create(self, settings_service: SettingsService):
# Here you would have logic to create and configure a CacheService # Here you would have logic to create and configure a CacheService
# based on the settings_service # based on the settings_service

View file

@ -187,7 +187,8 @@ class DatabaseService(Service):
await session.commit() await session.commit()
logger.debug("Successfully assigned orphaned flows to the default superuser") logger.debug("Successfully assigned orphaned flows to the default superuser")
def _generate_unique_flow_name(self, original_name: str, existing_names: set[str]) -> str: @staticmethod
def _generate_unique_flow_name(original_name: str, existing_names: set[str]) -> str:
"""Generate a unique flow name by adding or incrementing a suffix.""" """Generate a unique flow name by adding or incrementing a suffix."""
if original_name not in existing_names: if original_name not in existing_names:
return original_name return original_name
@ -212,7 +213,8 @@ class DatabaseService(Service):
return new_name return new_name
def _check_schema_health(self, connection) -> bool: @staticmethod
def _check_schema_health(connection) -> bool:
inspector = inspect(connection) inspector = inspect(connection)
model_mapping: dict[str, type[SQLModel]] = { model_mapping: dict[str, type[SQLModel]] = {
@ -249,7 +251,8 @@ class DatabaseService(Service):
async with self.with_session() as session, session.bind.connect() as conn: async with self.with_session() as session, session.bind.connect() as conn:
await conn.run_sync(self._check_schema_health) await conn.run_sync(self._check_schema_health)
def init_alembic(self, alembic_cfg) -> None: @staticmethod
def init_alembic(alembic_cfg) -> None:
logger.info("Initializing alembic") logger.info("Initializing alembic")
command.ensure_version(alembic_cfg) command.ensure_version(alembic_cfg)
# alembic_cfg.attributes["connection"].commit() # alembic_cfg.attributes["connection"].commit()
@ -317,7 +320,8 @@ class DatabaseService(Service):
should_initialize_alembic = True should_initialize_alembic = True
await asyncio.to_thread(self._run_migrations, should_initialize_alembic, fix) await asyncio.to_thread(self._run_migrations, should_initialize_alembic, fix)
def try_downgrade_upgrade_until_success(self, alembic_cfg, retries=5) -> None: @staticmethod
def try_downgrade_upgrade_until_success(alembic_cfg, retries=5) -> None:
# Try -1 then head, if it fails, try -2 then head, etc. # Try -1 then head, if it fails, try -2 then head, etc.
# until we reach the number of retries # until we reach the number of retries
for i in range(1, retries + 1): for i in range(1, retries + 1):

View file

@ -1,5 +1,7 @@
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from typing_extensions import override
from langflow.services.factory import ServiceFactory from langflow.services.factory import ServiceFactory
from langflow.services.session.service import SessionService from langflow.services.session.service import SessionService
@ -11,5 +13,6 @@ class SessionServiceFactory(ServiceFactory):
def __init__(self) -> None: def __init__(self) -> None:
super().__init__(SessionService) super().__init__(SessionService)
@override
def create(self, cache_service: "CacheService"): def create(self, cache_service: "CacheService"):
return SessionService(cache_service) return SessionService(cache_service)

View file

@ -38,7 +38,8 @@ class SessionService(Service):
return graph, artifacts return graph, artifacts
def build_key(self, session_id, data_graph) -> str: @staticmethod
def build_key(session_id, data_graph) -> str:
json_hash = compute_dict_hash(data_graph) json_hash = compute_dict_hash(data_graph)
return f"{session_id}{':' if session_id else ''}{json_hash}" return f"{session_id}{':' if session_id else ''}{json_hash}"

View file

@ -1,3 +1,5 @@
from typing_extensions import override
from langflow.services.factory import ServiceFactory from langflow.services.factory import ServiceFactory
from langflow.services.settings.service import SettingsService from langflow.services.settings.service import SettingsService
@ -13,6 +15,7 @@ class SettingsServiceFactory(ServiceFactory):
def __init__(self) -> None: def __init__(self) -> None:
super().__init__(SettingsService) super().__init__(SettingsService)
@override
def create(self): def create(self):
# Here you would have logic to create and configure a SettingsService # Here you would have logic to create and configure a SettingsService

View file

@ -1,5 +1,7 @@
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from typing_extensions import override
from langflow.services.factory import ServiceFactory from langflow.services.factory import ServiceFactory
from langflow.services.shared_component_cache.service import SharedComponentCacheService from langflow.services.shared_component_cache.service import SharedComponentCacheService
@ -11,5 +13,6 @@ class SharedComponentCacheServiceFactory(ServiceFactory):
def __init__(self) -> None: def __init__(self) -> None:
super().__init__(SharedComponentCacheService) super().__init__(SharedComponentCacheService)
@override
def create(self, settings_service: "SettingsService"): def create(self, settings_service: "SettingsService"):
return SharedComponentCacheService(expiration_time=settings_service.settings.cache_expire) return SharedComponentCacheService(expiration_time=settings_service.settings.cache_expire)

View file

@ -1,5 +1,7 @@
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from typing_extensions import override
from langflow.services.factory import ServiceFactory from langflow.services.factory import ServiceFactory
from langflow.services.socket.service import SocketIOService from langflow.services.socket.service import SocketIOService
@ -13,5 +15,6 @@ class SocketIOFactory(ServiceFactory):
service_class=SocketIOService, service_class=SocketIOService,
) )
@override
def create(self, cache_service: "CacheService"): def create(self, cache_service: "CacheService"):
return SocketIOService(cache_service) return SocketIOService(cache_service)

View file

@ -1,3 +1,5 @@
from typing_extensions import override
from langflow.services.factory import ServiceFactory from langflow.services.factory import ServiceFactory
from langflow.services.settings.service import SettingsService from langflow.services.settings.service import SettingsService
from langflow.services.state.service import InMemoryStateService from langflow.services.state.service import InMemoryStateService
@ -7,6 +9,7 @@ class StateServiceFactory(ServiceFactory):
def __init__(self) -> None: def __init__(self) -> None:
super().__init__(InMemoryStateService) super().__init__(InMemoryStateService)
@override
def create(self, settings_service: SettingsService): def create(self, settings_service: SettingsService):
return InMemoryStateService( return InMemoryStateService(
settings_service, settings_service,

View file

@ -1,4 +1,5 @@
from loguru import logger from loguru import logger
from typing_extensions import override
from langflow.services.factory import ServiceFactory from langflow.services.factory import ServiceFactory
from langflow.services.session.service import SessionService from langflow.services.session.service import SessionService
@ -12,6 +13,7 @@ class StorageServiceFactory(ServiceFactory):
StorageService, StorageService,
) )
@override
def create(self, session_service: SessionService, settings_service: SettingsService): def create(self, session_service: SessionService, settings_service: SettingsService):
storage_type = settings_service.settings.storage_type storage_type = settings_service.settings.storage_type
if storage_type.lower() == "local": if storage_type.lower() == "local":

View file

@ -21,12 +21,15 @@ class LocalStorageService(StorageService):
async def save_file(self, flow_id: str, file_name: str, data: bytes) -> None: async def save_file(self, flow_id: str, file_name: str, data: bytes) -> None:
"""Save a file in the local storage. """Save a file in the local storage.
:param flow_id: The identifier for the flow. Args:
:param file_name: The name of the file to be saved. flow_id: The identifier for the flow.
:param data: The byte content of the file. file_name: The name of the file to be saved.
:raises FileNotFoundError: If the specified flow does not exist. data: The byte content of the file.
:raises IsADirectoryError: If the file name is a directory.
:raises PermissionError: If there is no permission to write the file. Raises:
FileNotFoundError: If the specified flow does not exist.
IsADirectoryError: If the file name is a directory.
PermissionError: If there is no permission to write the file.
""" """
folder_path = self.data_dir / flow_id folder_path = self.data_dir / flow_id
await folder_path.mkdir(parents=True, exist_ok=True) await folder_path.mkdir(parents=True, exist_ok=True)
@ -43,10 +46,15 @@ class LocalStorageService(StorageService):
async def get_file(self, flow_id: str, file_name: str) -> bytes: async def get_file(self, flow_id: str, file_name: str) -> bytes:
"""Retrieve a file from the local storage. """Retrieve a file from the local storage.
:param flow_id: The identifier for the flow. Args:
:param file_name: The name of the file to be retrieved. flow_id: The identifier for the flow.
:return: The byte content of the file. file_name: The name of the file to be retrieved.
:raises FileNotFoundError: If the file does not exist.
Returns:
The byte content of the file.
Raises:
FileNotFoundError: If the file does not exist.
""" """
file_path = self.data_dir / flow_id / file_name file_path = self.data_dir / flow_id / file_name
if not await file_path.exists(): if not await file_path.exists():
@ -63,9 +71,14 @@ class LocalStorageService(StorageService):
async def list_files(self, flow_id: str): async def list_files(self, flow_id: str):
"""List all files in a specified flow. """List all files in a specified flow.
:param flow_id: The identifier for the flow. Args:
:return: A list of file names. flow_id: The identifier for the flow.
:raises FileNotFoundError: If the flow directory does not exist.
Returns:
A list of file names.
Raises:
FileNotFoundError: If the flow directory does not exist.
""" """
if not isinstance(flow_id, str): if not isinstance(flow_id, str):
flow_id = str(flow_id) flow_id = str(flow_id)

View file

@ -18,10 +18,13 @@ class S3StorageService(StorageService):
async def save_file(self, folder: str, file_name: str, data) -> None: async def save_file(self, folder: str, file_name: str, data) -> None:
"""Save a file to the S3 bucket. """Save a file to the S3 bucket.
:param folder: The folder in the bucket to save the file. Args:
:param file_name: The name of the file to be saved. folder: The folder in the bucket to save the file.
:param data: The byte content of the file. file_name: The name of the file to be saved.
:raises Exception: If an error occurs during file saving. data: The byte content of the file.
Raises:
Exception: If an error occurs during file saving.
""" """
try: try:
self.s3_client.put_object(Bucket=self.bucket, Key=f"{folder}/{file_name}", Body=data) self.s3_client.put_object(Bucket=self.bucket, Key=f"{folder}/{file_name}", Body=data)
@ -36,10 +39,15 @@ class S3StorageService(StorageService):
async def get_file(self, folder: str, file_name: str): async def get_file(self, folder: str, file_name: str):
"""Retrieve a file from the S3 bucket. """Retrieve a file from the S3 bucket.
:param folder: The folder in the bucket where the file is stored. Args:
:param file_name: The name of the file to be retrieved. folder: The folder in the bucket where the file is stored.
:return: The byte content of the file. file_name: The name of the file to be retrieved.
:raises Exception: If an error occurs during file retrieval.
Returns:
The byte content of the file.
Raises:
Exception: If an error occurs during file retrieval.
""" """
try: try:
response = self.s3_client.get_object(Bucket=self.bucket, Key=f"{folder}/{file_name}") response = self.s3_client.get_object(Bucket=self.bucket, Key=f"{folder}/{file_name}")
@ -52,9 +60,14 @@ class S3StorageService(StorageService):
async def list_files(self, folder: str): async def list_files(self, folder: str):
"""List all files in a specified folder of the S3 bucket. """List all files in a specified folder of the S3 bucket.
:param folder: The folder in the bucket to list files from. Args:
:return: A list of file names. folder: The folder in the bucket to list files from.
:raises Exception: If an error occurs during file listing.
Returns:
A list of file names.
Raises:
Exception: If an error occurs during file listing.
""" """
try: try:
response = self.s3_client.list_objects_v2(Bucket=self.bucket, Prefix=folder) response = self.s3_client.list_objects_v2(Bucket=self.bucket, Prefix=folder)
@ -69,9 +82,12 @@ class S3StorageService(StorageService):
async def delete_file(self, folder: str, file_name: str) -> None: async def delete_file(self, folder: str, file_name: str) -> None:
"""Delete a file from the S3 bucket. """Delete a file from the S3 bucket.
:param folder: The folder in the bucket where the file is stored. Args:
:param file_name: The name of the file to be deleted. folder: The folder in the bucket where the file is stored.
:raises Exception: If an error occurs during file deletion. file_name: The name of the file to be deleted.
Raises:
Exception: If an error occurs during file deletion.
""" """
try: try:
self.s3_client.delete_object(Bucket=self.bucket, Key=f"{folder}/{file_name}") self.s3_client.delete_object(Bucket=self.bucket, Key=f"{folder}/{file_name}")

View file

@ -2,6 +2,8 @@ from __future__ import annotations
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from typing_extensions import override
from langflow.services.factory import ServiceFactory from langflow.services.factory import ServiceFactory
from langflow.services.store.service import StoreService from langflow.services.store.service import StoreService
@ -13,5 +15,6 @@ class StoreServiceFactory(ServiceFactory):
def __init__(self) -> None: def __init__(self) -> None:
super().__init__(StoreService) super().__init__(StoreService)
@override
def create(self, settings_service: SettingsService): def create(self, settings_service: SettingsService):
return StoreService(settings_service) return StoreService(settings_service)

View file

@ -164,7 +164,8 @@ class StoreService(Service):
except Exception: # noqa: BLE001 except Exception: # noqa: BLE001
logger.opt(exception=True).debug("Webhook failed") logger.opt(exception=True).debug("Webhook failed")
def build_tags_filter(self, tags: list[str]): @staticmethod
def build_tags_filter(tags: list[str]):
tags_filter: dict[str, Any] = {"tags": {"_and": []}} tags_filter: dict[str, Any] = {"tags": {"_and": []}}
for tag in tags: for tag in tags:
tags_filter["tags"]["_and"].append({"_some": {"tags_id": {"name": {"_eq": tag}}}}) tags_filter["tags"]["_and"].append({"_some": {"tags_id": {"name": {"_eq": tag}}}})
@ -249,7 +250,8 @@ class StoreService(Service):
return filter_conditions return filter_conditions
def build_liked_filter(self): @staticmethod
def build_liked_filter():
user_data = user_data_var.get() user_data = user_data_var.get()
# params["filter"] = json.dumps({"user_created": {"_eq": user_data["id"]}}) # params["filter"] = json.dumps({"user_created": {"_eq": user_data["id"]}})
if not user_data: if not user_data:

View file

@ -1,3 +1,5 @@
from typing_extensions import override
from langflow.services.factory import ServiceFactory from langflow.services.factory import ServiceFactory
from langflow.services.task.service import TaskService from langflow.services.task.service import TaskService
@ -6,6 +8,7 @@ class TaskServiceFactory(ServiceFactory):
def __init__(self) -> None: def __init__(self) -> None:
super().__init__(TaskService) super().__init__(TaskService)
@override
def create(self): def create(self):
# Here you would have logic to create and configure a TaskService # Here you would have logic to create and configure a TaskService
return TaskService() return TaskService()

View file

@ -2,6 +2,8 @@ from __future__ import annotations
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from typing_extensions import override
from langflow.services.factory import ServiceFactory from langflow.services.factory import ServiceFactory
from langflow.services.telemetry.service import TelemetryService from langflow.services.telemetry.service import TelemetryService
@ -13,5 +15,6 @@ class TelemetryServiceFactory(ServiceFactory):
def __init__(self) -> None: def __init__(self) -> None:
super().__init__(TelemetryService) super().__init__(TelemetryService)
@override
def create(self, settings_service: SettingsService): def create(self, settings_service: SettingsService):
return TelemetryService(settings_service) return TelemetryService(settings_service)

View file

@ -132,7 +132,8 @@ class TelemetryService(Service):
except Exception: # noqa: BLE001 except Exception: # noqa: BLE001
logger.exception("Error flushing logs") logger.exception("Error flushing logs")
async def _cancel_task(self, task: asyncio.Task, cancel_msg: str) -> None: @staticmethod
async def _cancel_task(task: asyncio.Task, cancel_msg: str) -> None:
task.cancel(cancel_msg) task.cancel(cancel_msg)
await asyncio.wait([task]) await asyncio.wait([task])
if not task.cancelled(): if not task.cancelled():

View file

@ -319,7 +319,8 @@ class ArizePhoenixTracer(BaseTracer):
return value return value
def _error_to_string(self, error: Exception | None): @staticmethod
def _error_to_string(error: Exception | None):
"""Converts an error to a string with traceback details.""" """Converts an error to a string with traceback details."""
error_message = None error_message = None
if error: if error:
@ -327,11 +328,13 @@ class ArizePhoenixTracer(BaseTracer):
error_message = f"{error.__class__.__name__}: {error}\n\n{string_stacktrace}" error_message = f"{error.__class__.__name__}: {error}\n\n{string_stacktrace}"
return error_message return error_message
def _get_current_timestamp(self) -> int: @staticmethod
def _get_current_timestamp() -> int:
"""Gets the current UTC timestamp in nanoseconds.""" """Gets the current UTC timestamp in nanoseconds."""
return int(datetime.now(timezone.utc).timestamp() * 1_000_000_000) return int(datetime.now(timezone.utc).timestamp() * 1_000_000_000)
def _safe_json_dumps(self, obj: Any, **kwargs: Any) -> str: @staticmethod
def _safe_json_dumps(obj: Any, **kwargs: Any) -> str:
"""A convenience wrapper around `json.dumps` that ensures that any object can be safely encoded.""" """A convenience wrapper around `json.dumps` that ensures that any object can be safely encoded."""
return json.dumps(obj, default=str, ensure_ascii=False, **kwargs) return json.dumps(obj, default=str, ensure_ascii=False, **kwargs)
@ -359,6 +362,7 @@ class ArizePhoenixTracer(BaseTracer):
else: else:
current_span.set_status(Status(StatusCode.OK)) current_span.set_status(Status(StatusCode.OK))
@override
def get_langchain_callback(self) -> BaseCallbackHandler | None: def get_langchain_callback(self) -> BaseCallbackHandler | None:
"""Returns the LangChain callback handler if applicable.""" """Returns the LangChain callback handler if applicable."""
return None return None

View file

@ -2,6 +2,8 @@ from __future__ import annotations
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from typing_extensions import override
from langflow.services.factory import ServiceFactory from langflow.services.factory import ServiceFactory
from langflow.services.tracing.service import TracingService from langflow.services.tracing.service import TracingService
@ -13,5 +15,6 @@ class TracingServiceFactory(ServiceFactory):
def __init__(self) -> None: def __init__(self) -> None:
super().__init__(TracingService) super().__init__(TracingService)
@override
def create(self, settings_service: SettingsService): def create(self, settings_service: SettingsService):
return TracingService(settings_service) return TracingService(settings_service)

View file

@ -135,7 +135,8 @@ class LangFuseTracer(BaseTracer):
return None return None
return None # self._callback return None # self._callback
def _get_config(self) -> dict: @staticmethod
def _get_config() -> dict:
secret_key = os.getenv("LANGFUSE_SECRET_KEY", None) secret_key = os.getenv("LANGFUSE_SECRET_KEY", None)
public_key = os.getenv("LANGFUSE_PUBLIC_KEY", None) public_key = os.getenv("LANGFUSE_PUBLIC_KEY", None)
host = os.getenv("LANGFUSE_HOST", None) host = os.getenv("LANGFUSE_HOST", None)

View file

@ -7,6 +7,7 @@ from datetime import datetime, timezone
from typing import TYPE_CHECKING, Any from typing import TYPE_CHECKING, Any
from loguru import logger from loguru import logger
from typing_extensions import override
from langflow.schema.data import Data from langflow.schema.data import Data
from langflow.services.tracing.base import BaseTracer from langflow.services.tracing.base import BaseTracer
@ -140,7 +141,8 @@ class LangSmithTracer(BaseTracer):
child.post() child.post()
self._child_link[trace_name] = child.get_url() self._child_link[trace_name] = child.get_url()
def _error_to_string(self, error: Exception | None): @staticmethod
def _error_to_string(error: Exception | None):
error_message = None error_message = None
if error: if error:
string_stacktrace = traceback.format_exception(error) string_stacktrace = traceback.format_exception(error)
@ -163,5 +165,6 @@ class LangSmithTracer(BaseTracer):
self._run_tree.post() self._run_tree.post()
self._run_link = self._run_tree.get_url() self._run_link = self._run_tree.get_url()
@override
def get_langchain_callback(self) -> BaseCallbackHandler | None: def get_langchain_callback(self) -> BaseCallbackHandler | None:
return None return None

View file

@ -264,7 +264,8 @@ class TracingService(Service):
self.outputs[trace_name] |= outputs or {} self.outputs[trace_name] |= outputs or {}
self.outputs_metadata[trace_name] |= output_metadata or {} self.outputs_metadata[trace_name] |= output_metadata or {}
def _cleanup_inputs(self, inputs: dict[str, Any]): @staticmethod
def _cleanup_inputs(inputs: dict[str, Any]):
inputs = inputs.copy() inputs = inputs.copy()
for key in inputs: for key in inputs:
if "api_key" in key: if "api_key" in key:

View file

@ -177,9 +177,6 @@ async def clean_transactions(settings_service: SettingsService, session: AsyncSe
Args: Args:
settings_service: The settings service containing configuration like max_transactions_to_keep settings_service: The settings service containing configuration like max_transactions_to_keep
session: The database session to use for the deletion session: The database session to use for the deletion
Returns:
None
""" """
try: try:
# Delete transactions using bulk delete # Delete transactions using bulk delete
@ -209,9 +206,6 @@ async def clean_vertex_builds(settings_service: SettingsService, session: AsyncS
Args: Args:
settings_service: The settings service containing configuration like max_vertex_builds_to_keep settings_service: The settings service containing configuration like max_vertex_builds_to_keep
session: The database session to use for the deletion session: The database session to use for the deletion
Returns:
None
""" """
try: try:
# Delete vertex builds using bulk delete # Delete vertex builds using bulk delete

View file

@ -2,6 +2,8 @@ from __future__ import annotations
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from typing_extensions import override
from langflow.services.factory import ServiceFactory from langflow.services.factory import ServiceFactory
from langflow.services.variable.service import DatabaseVariableService, VariableService from langflow.services.variable.service import DatabaseVariableService, VariableService
@ -13,6 +15,7 @@ class VariableServiceFactory(ServiceFactory):
def __init__(self) -> None: def __init__(self) -> None:
super().__init__(VariableService) super().__init__(VariableService)
@override
def create(self, settings_service: SettingsService): def create(self, settings_service: SettingsService):
# here you would have logic to create and configure a VariableService # here you would have logic to create and configure a VariableService
# based on the settings_service # based on the settings_service

View file

@ -6,6 +6,7 @@ from typing import TYPE_CHECKING
from loguru import logger from loguru import logger
from sqlmodel import select from sqlmodel import select
from typing_extensions import override
from langflow.services.auth import utils as auth_utils from langflow.services.auth import utils as auth_utils
from langflow.services.base import Service from langflow.services.base import Service
@ -141,6 +142,7 @@ class DatabaseVariableService(VariableService, Service):
await session.refresh(db_variable) await session.refresh(db_variable)
return db_variable return db_variable
@override
async def delete_variable( async def delete_variable(
self, self,
user_id: UUID | str, user_id: UUID | str,
@ -155,6 +157,7 @@ class DatabaseVariableService(VariableService, Service):
await session.delete(variable) await session.delete(variable)
await session.commit() await session.commit()
@override
async def delete_variable_by_id(self, user_id: UUID | str, variable_id: UUID, session: AsyncSession) -> None: async def delete_variable_by_id(self, user_id: UUID | str, variable_id: UUID, session: AsyncSession) -> None:
stmt = select(Variable).where(Variable.user_id == user_id, Variable.id == variable_id) stmt = select(Variable).where(Variable.user_id == user_id, Variable.id == variable_id)
variable = (await session.exec(stmt)).first() variable = (await session.exec(stmt)).first()

View file

@ -36,7 +36,8 @@ class KeyedWorkerLockManager:
def __init__(self) -> None: def __init__(self) -> None:
self.locks_dir = Path(user_cache_dir("langflow"), ensure_exists=True) / "worker_locks" self.locks_dir = Path(user_cache_dir("langflow"), ensure_exists=True) / "worker_locks"
def _validate_key(self, key: str) -> bool: @staticmethod
def _validate_key(key: str) -> bool:
"""Validate that the string only contains alphanumeric characters and underscores. """Validate that the string only contains alphanumeric characters and underscores.
Parameters: Parameters:

View file

@ -12,6 +12,7 @@ from docstring_parser import parse
from langflow.logging.logger import logger from langflow.logging.logger import logger
from langflow.schema import Data from langflow.schema import Data
from langflow.services.deps import get_settings_service from langflow.services.deps import get_settings_service
from langflow.services.utils import initialize_settings_service
from langflow.template.frontend_node.constants import FORCE_SHOW_FIELDS from langflow.template.frontend_node.constants import FORCE_SHOW_FIELDS
from langflow.utils import constants from langflow.utils import constants
@ -416,8 +417,6 @@ async def update_settings(
max_file_size_upload: int = 100, max_file_size_upload: int = 100,
) -> None: ) -> None:
"""Update the settings from a config file.""" """Update the settings from a config file."""
from langflow.services.utils import initialize_settings_service
# Check for database_url in the environment variables # Check for database_url in the environment variables
initialize_settings_service() initialize_settings_service()

View file

@ -4,7 +4,11 @@ from langflow.utils import constants
def truncate_long_strings(data, max_length=None): def truncate_long_strings(data, max_length=None):
"""Recursively traverse the dictionary or list and truncate strings longer than max_length.""" """Recursively traverse the dictionary or list and truncate strings longer than max_length.
Returns:
The data with strings truncated if they exceed the max length.
"""
if max_length is None: if max_length is None:
max_length = constants.MAX_TEXT_LENGTH max_length = constants.MAX_TEXT_LENGTH

View file

@ -170,9 +170,15 @@ def create_function(code, function_name):
def create_class(code, class_name): def create_class(code, class_name):
"""Dynamically create a class from a string of code and a specified class name. """Dynamically create a class from a string of code and a specified class name.
:param code: String containing the Python code defining the class Args:
:param class_name: Name of the class to be created code: String containing the Python code defining the class
:return: A function that, when called, returns an instance of the created class class_name: Name of the class to be created
Returns:
A function that, when called, returns an instance of the created class
Raises:
ValueError: If the code contains syntax errors or the class definition is invalid
""" """
if not hasattr(ast, "TypeIgnore"): if not hasattr(ast, "TypeIgnore"):
ast.TypeIgnore = create_type_ignore_class() ast.TypeIgnore = create_type_ignore_class()
@ -199,7 +205,8 @@ def create_class(code, class_name):
def create_type_ignore_class(): def create_type_ignore_class():
"""Create a TypeIgnore class for AST module if it doesn't exist. """Create a TypeIgnore class for AST module if it doesn't exist.
:return: TypeIgnore class Returns:
TypeIgnore class
""" """
class TypeIgnore(ast.AST): class TypeIgnore(ast.AST):
@ -211,8 +218,15 @@ def create_type_ignore_class():
def prepare_global_scope(code, module): def prepare_global_scope(code, module):
"""Prepares the global scope with necessary imports from the provided code module. """Prepares the global scope with necessary imports from the provided code module.
:param module: AST parsed module Args:
:return: Dictionary representing the global scope with imported modules code: The Python code
module: AST parsed module
Returns:
Dictionary representing the global scope with imported modules
Raises:
ModuleNotFoundError: If a module is not found in the code
""" """
exec_globals = globals().copy() exec_globals = globals().copy()
exec_globals.update(get_default_imports(code)) exec_globals.update(get_default_imports(code))
@ -250,9 +264,12 @@ def prepare_global_scope(code, module):
def extract_class_code(module, class_name): def extract_class_code(module, class_name):
"""Extracts the AST node for the specified class from the module. """Extracts the AST node for the specified class from the module.
:param module: AST parsed module Args:
:param class_name: Name of the class to extract module: AST parsed module
:return: AST node of the specified class class_name: Name of the class to extract
Returns:
AST node of the specified class
""" """
class_code = next(node for node in module.body if isinstance(node, ast.ClassDef) and node.name == class_name) class_code = next(node for node in module.body if isinstance(node, ast.ClassDef) and node.name == class_name)
@ -263,8 +280,11 @@ def extract_class_code(module, class_name):
def compile_class_code(class_code): def compile_class_code(class_code):
"""Compiles the AST node of a class into a code object. """Compiles the AST node of a class into a code object.
:param class_code: AST node of the class Args:
:return: Compiled code object of the class class_code: AST node of the class
Returns:
Compiled code object of the class
""" """
return compile(ast.Module(body=[class_code], type_ignores=[]), "<string>", "exec") return compile(ast.Module(body=[class_code], type_ignores=[]), "<string>", "exec")
@ -272,10 +292,13 @@ def compile_class_code(class_code):
def build_class_constructor(compiled_class, exec_globals, class_name): def build_class_constructor(compiled_class, exec_globals, class_name):
"""Builds a constructor function for the dynamically created class. """Builds a constructor function for the dynamically created class.
:param compiled_class: Compiled code object of the class Args:
:param exec_globals: Global scope with necessary imports compiled_class: Compiled code object of the class
:param class_name: Name of the class exec_globals: Global scope with necessary imports
:return: Constructor function for the class class_name: Name of the class
Returns:
Constructor function for the class
""" """
exec(compiled_class, exec_globals, locals()) exec(compiled_class, exec_globals, locals())
exec_globals[class_name] = locals()[class_name] exec_globals[class_name] = locals()[class_name]
@ -313,9 +336,12 @@ def get_default_imports(code_string):
def find_names_in_code(code, names): def find_names_in_code(code, names):
"""Finds if any of the specified names are present in the given code string. """Finds if any of the specified names are present in the given code string.
:param code: The source code as a string. Args:
:param names: A list of names to check for in the code. code: The source code as a string.
:return: A set of names that are found in the code. names: A list of names to check for in the code.
Returns:
A set of names that are found in the code.
""" """
return {name for name in names if name in code} return {name for name in names if name in code}
@ -339,7 +365,7 @@ def extract_class_name(code: str) -> str:
str: Name of the first Component subclass found str: Name of the first Component subclass found
Raises: Raises:
ValueError: If no Component subclass is found in the code TypeError: If no Component subclass is found in the code
""" """
try: try:
module = ast.parse(code) module = ast.parse(code)

View file

@ -1,4 +1,7 @@
from importlib import metadata
import httpx import httpx
from packaging import version as pkg_version
from langflow.logging.logger import logger from langflow.logging.logger import logger
@ -22,8 +25,6 @@ def _get_version_info():
Raises: Raises:
ValueError: If the package is not found from the list of package names. ValueError: If the package is not found from the list of package names.
""" """
from importlib import metadata
package_options = [ package_options = [
("langflow", "Langflow"), ("langflow", "Langflow"),
("langflow-base", "Langflow Base"), ("langflow-base", "Langflow Base"),
@ -57,8 +58,9 @@ VERSION_INFO = _get_version_info()
def is_pre_release(v: str) -> bool: def is_pre_release(v: str) -> bool:
"""Whether the version is a pre-release version. """Whether the version is a pre-release version.
Returns a boolean indicating whether the version is a pre-release version, Returns:
as per the definition of a pre-release segment from PEP 440. Whether the version is a pre-release version,
as per the definition of a pre-release segment from PEP 440.
""" """
return any(label in v for label in ["a", "b", "rc"]) return any(label in v for label in ["a", "b", "rc"])
@ -66,15 +68,14 @@ def is_pre_release(v: str) -> bool:
def is_nightly(v: str) -> bool: def is_nightly(v: str) -> bool:
"""Whether the version is a dev (nightly) version. """Whether the version is a dev (nightly) version.
Returns a boolean indicating whether the version is a dev (nightly) version, Returns:
as per the definition of a dev segment from PEP 440. Whether the version is a dev (nightly) version,
as per the definition of a dev segment from PEP 440.
""" """
return "dev" in v return "dev" in v
def fetch_latest_version(package_name: str, *, include_prerelease: bool) -> str | None: def fetch_latest_version(package_name: str, *, include_prerelease: bool) -> str | None:
from packaging import version as pkg_version
package_name = package_name.replace(" ", "-").lower() package_name = package_name.replace(" ", "-").lower()
try: try:
response = httpx.get(f"https://pypi.org/pypi/{package_name}/json") response = httpx.get(f"https://pypi.org/pypi/{package_name}/json")

View file

@ -18,7 +18,11 @@ def test_celery(word: str) -> str:
@celery_app.task(bind=True, soft_time_limit=30, max_retries=3) @celery_app.task(bind=True, soft_time_limit=30, max_retries=3)
def build_vertex(self, vertex: Vertex) -> Vertex: def build_vertex(self, vertex: Vertex) -> Vertex:
"""Build a vertex.""" """Build a vertex.
Returns:
The built vertex.
"""
try: try:
vertex.task_id = self.request.id vertex.task_id = self.request.id
async_to_sync(vertex.build)() async_to_sync(vertex.build)()

View file

@ -1,6 +1,7 @@
"""Module for package versioning.""" """Module for package versioning."""
import contextlib import contextlib
from importlib import metadata
def get_version() -> str: def get_version() -> str:
@ -14,8 +15,6 @@ def get_version() -> str:
Raises: Raises:
ValueError: If the package is not found from the list of package names. ValueError: If the package is not found from the list of package names.
""" """
from importlib import metadata
pkg_names = [ pkg_names = [
"langflow", "langflow",
"langflow-base", "langflow-base",

View file

@ -17,8 +17,10 @@ from blockbuster import blockbuster_ctx
from dotenv import load_dotenv from dotenv import load_dotenv
from fastapi.testclient import TestClient from fastapi.testclient import TestClient
from httpx import ASGITransport, AsyncClient from httpx import ASGITransport, AsyncClient
from langflow.components.inputs import ChatInput
from langflow.graph import Graph from langflow.graph import Graph
from langflow.initial_setup.constants import STARTER_FOLDER_NAME from langflow.initial_setup.constants import STARTER_FOLDER_NAME
from langflow.main import create_app
from langflow.services.auth.utils import get_password_hash from langflow.services.auth.utils import get_password_hash
from langflow.services.database.models.api_key.model import ApiKey from langflow.services.database.models.api_key.model import ApiKey
from langflow.services.database.models.flow.model import Flow, FlowCreate from langflow.services.database.models.flow.model import Flow, FlowCreate
@ -155,8 +157,6 @@ def caplog(caplog: pytest.LogCaptureFixture):
@pytest.fixture @pytest.fixture
async def async_client() -> AsyncGenerator: async def async_client() -> AsyncGenerator:
from langflow.main import create_app
app = create_app() app = create_app()
async with AsyncClient(app=app, base_url="http://testserver", http2=True) as client: async with AsyncClient(app=app, base_url="http://testserver", http2=True) as client:
yield client yield client
@ -228,8 +228,6 @@ def distributed_client_fixture(
# def get_session_override(): # def get_session_override():
# return session # return session
from langflow.main import create_app
app = create_app() app = create_app()
# app.dependency_overrides[get_session] = get_session_override # app.dependency_overrides[get_session] = get_session_override
@ -357,8 +355,6 @@ async def client_fixture(
monkeypatch.setenv("LANGFLOW_LOAD_FLOWS_PATH", load_flows_dir) monkeypatch.setenv("LANGFLOW_LOAD_FLOWS_PATH", load_flows_dir)
monkeypatch.setenv("LANGFLOW_AUTO_LOGIN", "true") monkeypatch.setenv("LANGFLOW_AUTO_LOGIN", "true")
from langflow.main import create_app
app = create_app() app = create_app()
db_service = get_db_service() db_service = get_db_service()
db_service.database_url = f"sqlite:///{db_path}" db_service.database_url = f"sqlite:///{db_path}"
@ -482,8 +478,6 @@ async def flow(
json_flow: str, json_flow: str,
active_user, active_user,
): ):
from langflow.services.database.models.flow.model import FlowCreate
loaded_json = json.loads(json_flow) loaded_json = json.loads(json_flow)
flow_data = FlowCreate(name="test_flow", data=loaded_json.get("data"), user_id=active_user.id) flow_data = FlowCreate(name="test_flow", data=loaded_json.get("data"), user_id=active_user.id)
@ -577,8 +571,6 @@ async def added_webhook_test(client, json_webhook_test, logged_in_headers):
@pytest.fixture @pytest.fixture
async def flow_component(client: AsyncClient, logged_in_headers): async def flow_component(client: AsyncClient, logged_in_headers):
from langflow.components.inputs import ChatInput
chat_input = ChatInput() chat_input = ChatInput()
graph = Graph(start=chat_input, end=chat_input) graph = Graph(start=chat_input, end=chat_input)
graph_dict = graph.dump(name="Chat Input Component") graph_dict = graph.dump(name="Chat Input Component")

View file

@ -1,12 +1,17 @@
import pytest import pytest
from langflow.components.astra_assistants import (
AssistantsCreateAssistant,
AssistantsCreateThread,
AssistantsGetAssistantName,
AssistantsListAssistants,
AssistantsRun,
)
from tests.integration.utils import run_single_component from tests.integration.utils import run_single_component
@pytest.mark.api_key_required @pytest.mark.api_key_required
async def test_list_assistants(): async def test_list_assistants():
from langflow.components.astra_assistants import AssistantsListAssistants
results = await run_single_component( results = await run_single_component(
AssistantsListAssistants, AssistantsListAssistants,
inputs={}, inputs={},
@ -16,8 +21,6 @@ async def test_list_assistants():
@pytest.mark.api_key_required @pytest.mark.api_key_required
async def test_create_assistants(): async def test_create_assistants():
from langflow.components.astra_assistants import AssistantsCreateAssistant
results = await run_single_component( results = await run_single_component(
AssistantsCreateAssistant, AssistantsCreateAssistant,
inputs={ inputs={
@ -36,8 +39,6 @@ async def test_create_assistants():
@pytest.mark.api_key_required @pytest.mark.api_key_required
async def test_create_thread(): async def test_create_thread():
from langflow.components.astra_assistants import AssistantsCreateThread
results = await run_single_component( results = await run_single_component(
AssistantsCreateThread, AssistantsCreateThread,
inputs={}, inputs={},
@ -48,8 +49,6 @@ async def test_create_thread():
async def get_assistant_name(assistant_id): async def get_assistant_name(assistant_id):
from langflow.components.astra_assistants import AssistantsGetAssistantName
results = await run_single_component( results = await run_single_component(
AssistantsGetAssistantName, AssistantsGetAssistantName,
inputs={ inputs={
@ -60,8 +59,6 @@ async def get_assistant_name(assistant_id):
async def run_assistant(assistant_id, thread_id): async def run_assistant(assistant_id, thread_id):
from langflow.components.astra_assistants import AssistantsRun
results = await run_single_component( results = await run_single_component(
AssistantsRun, AssistantsRun,
inputs={ inputs={

View file

@ -2,6 +2,7 @@ import os
import pytest import pytest
from astrapy.db import AstraDB from astrapy.db import AstraDB
from langchain_astradb import AstraDBVectorStore, CollectionVectorServiceOptions
from langchain_core.documents import Document from langchain_core.documents import Document
from langflow.components.embeddings import OpenAIEmbeddingsComponent from langflow.components.embeddings import OpenAIEmbeddingsComponent
from langflow.components.vectorstores import AstraDBVectorStoreComponent from langflow.components.vectorstores import AstraDBVectorStoreComponent
@ -37,8 +38,6 @@ def astradb_client():
@pytest.mark.api_key_required @pytest.mark.api_key_required
async def test_base(astradb_client: AstraDB): async def test_base(astradb_client: AstraDB):
from langflow.components.embeddings import OpenAIEmbeddingsComponent
application_token = get_astradb_application_token() application_token = get_astradb_application_token()
api_endpoint = get_astradb_api_endpoint() api_endpoint = get_astradb_api_endpoint()
@ -88,8 +87,6 @@ async def test_astra_embeds_and_search():
@pytest.mark.api_key_required @pytest.mark.api_key_required
def test_astra_vectorize(): def test_astra_vectorize():
from langchain_astradb import AstraDBVectorStore, CollectionVectorServiceOptions
application_token = get_astradb_application_token() application_token = get_astradb_application_token()
api_endpoint = get_astradb_api_endpoint() api_endpoint = get_astradb_api_endpoint()
@ -132,8 +129,6 @@ def test_astra_vectorize():
@pytest.mark.api_key_required @pytest.mark.api_key_required
def test_astra_vectorize_with_provider_api_key(): def test_astra_vectorize_with_provider_api_key():
"""Tests vectorize using an openai api key.""" """Tests vectorize using an openai api key."""
from langchain_astradb import AstraDBVectorStore, CollectionVectorServiceOptions
application_token = get_astradb_application_token() application_token = get_astradb_application_token()
api_endpoint = get_astradb_api_endpoint() api_endpoint = get_astradb_api_endpoint()
@ -189,8 +184,6 @@ def test_astra_vectorize_with_provider_api_key():
@pytest.mark.api_key_required @pytest.mark.api_key_required
def test_astra_vectorize_passes_authentication(): def test_astra_vectorize_passes_authentication():
"""Tests vectorize using the authentication parameter.""" """Tests vectorize using the authentication parameter."""
from langchain_astradb import AstraDBVectorStore, CollectionVectorServiceOptions
store = None store = None
try: try:
application_token = get_astradb_application_token() application_token = get_astradb_application_token()

View file

@ -10,7 +10,7 @@ from tests.integration.utils import ComponentInputHandle, run_single_component
@pytest.mark.api_key_required @pytest.mark.api_key_required
async def test_csv_output_parser_openai(): async def test_csv_output_parser_openai():
format_instructions = ComponentInputHandle( format_instructions_ = ComponentInputHandle(
clazz=OutputParserComponent, clazz=OutputParserComponent,
inputs={}, inputs={},
output_name="format_instructions", output_name="format_instructions",
@ -24,7 +24,7 @@ async def test_csv_output_parser_openai():
clazz=PromptComponent, clazz=PromptComponent,
inputs={ inputs={
"template": "List the first five positive integers.\n\n{format_instructions}", "template": "List the first five positive integers.\n\n{format_instructions}",
"format_instructions": format_instructions, "format_instructions": format_instructions_,
}, },
output_name="prompt", output_name="prompt",
) )

View file

@ -28,7 +28,7 @@ async def test_initialize_services():
@pytest.mark.benchmark @pytest.mark.benchmark
async def test_setup_llm_caching(): def test_setup_llm_caching():
"""Benchmark LLM caching setup.""" """Benchmark LLM caching setup."""
from langflow.interface.utils import setup_llm_caching from langflow.interface.utils import setup_llm_caching

View file

@ -11,6 +11,7 @@ import anyio
import pytest import pytest
from asgi_lifespan import LifespanManager from asgi_lifespan import LifespanManager
from httpx import ASGITransport, AsyncClient from httpx import ASGITransport, AsyncClient
from langflow.main import create_app
from langflow.services.deps import get_storage_service from langflow.services.deps import get_storage_service
from langflow.services.storage.service import StorageService from langflow.services.storage.service import StorageService
from sqlmodel import Session from sqlmodel import Session
@ -60,8 +61,6 @@ async def files_client_fixture(
monkeypatch.setenv("LANGFLOW_LOAD_FLOWS_PATH", load_flows_dir) monkeypatch.setenv("LANGFLOW_LOAD_FLOWS_PATH", load_flows_dir)
monkeypatch.setenv("LANGFLOW_AUTO_LOGIN", "true") monkeypatch.setenv("LANGFLOW_AUTO_LOGIN", "true")
from langflow.main import create_app
app = create_app() app = create_app()
return app, db_path return app, db_path
@ -81,14 +80,14 @@ async def files_client_fixture(
@pytest.fixture @pytest.fixture
async def max_file_size_upload_fixture(monkeypatch): def max_file_size_upload_fixture(monkeypatch):
monkeypatch.setenv("LANGFLOW_MAX_FILE_SIZE_UPLOAD", "1") monkeypatch.setenv("LANGFLOW_MAX_FILE_SIZE_UPLOAD", "1")
yield yield
monkeypatch.undo() monkeypatch.undo()
@pytest.fixture @pytest.fixture
async def max_file_size_upload_10mb_fixture(monkeypatch): def max_file_size_upload_10mb_fixture(monkeypatch):
monkeypatch.setenv("LANGFLOW_MAX_FILE_SIZE_UPLOAD", "10") monkeypatch.setenv("LANGFLOW_MAX_FILE_SIZE_UPLOAD", "10")
yield yield
monkeypatch.undo() monkeypatch.undo()

View file

@ -11,7 +11,7 @@ from langflow.schema.data import Data
from pydantic import BaseModel from pydantic import BaseModel
async def test_component_tool(): def test_component_tool():
calculator_component = CalculatorToolComponent() calculator_component = CalculatorToolComponent()
component_toolkit = ComponentToolkit(component=calculator_component) component_toolkit = ComponentToolkit(component=calculator_component)
component_tool = component_toolkit.get_tools()[0] component_tool = component_toolkit.get_tools()[0]

View file

@ -118,7 +118,7 @@ class AllInputsComponent(Component):
return data return data
async def test_component_inputs_toolkit(): def test_component_inputs_toolkit():
component = AllInputsComponent() component = AllInputsComponent()
component_toolkit = ComponentToolkit(component=component) component_toolkit = ComponentToolkit(component=component)
component_tool = component_toolkit.get_tools()[0] component_tool = component_toolkit.get_tools()[0]

View file

@ -1,8 +1,11 @@
import re
from unittest.mock import MagicMock, patch from unittest.mock import MagicMock, patch
import pytest import pytest
from langchain_core.language_models import BaseLanguageModel from langchain_core.language_models import BaseLanguageModel
from langflow.components.helpers.structured_output import StructuredOutputComponent from langflow.components.helpers.structured_output import StructuredOutputComponent
from langflow.helpers.base_model import build_model_from_schema
from langflow.inputs.inputs import TableInput
from langflow.schema.data import Data from langflow.schema.data import Data
from pydantic import BaseModel from pydantic import BaseModel
from typing_extensions import override from typing_extensions import override
@ -12,8 +15,6 @@ class TestStructuredOutputComponent:
# Ensure that the structured output is successfully generated with the correct BaseModel instance returned by # Ensure that the structured output is successfully generated with the correct BaseModel instance returned by
# the mock function # the mock function
def test_successful_structured_output_generation_with_patch_with_config(self): def test_successful_structured_output_generation_with_patch_with_config(self):
from unittest.mock import patch
class MockLanguageModel(BaseLanguageModel): class MockLanguageModel(BaseLanguageModel):
@override @override
def with_structured_output(self, *args, **kwargs): def with_structured_output(self, *args, **kwargs):
@ -87,15 +88,11 @@ class TestStructuredOutputComponent:
multiple=False, multiple=False,
) )
with pytest.raises(TypeError, match="Language model does not support structured output."): with pytest.raises(TypeError, match=re.escape("Language model does not support structured output.")):
component.build_structured_output() component.build_structured_output()
# Correctly builds the output model from the provided schema # Correctly builds the output model from the provided schema
def test_correctly_builds_output_model(self): def test_correctly_builds_output_model(self):
# Import internal organization modules, packages, and libraries
from langflow.helpers.base_model import build_model_from_schema
from langflow.inputs.inputs import TableInput
# Setup # Setup
component = StructuredOutputComponent() component = StructuredOutputComponent()
schema = [ schema = [
@ -134,10 +131,6 @@ class TestStructuredOutputComponent:
# Properly handles multiple outputs when 'multiple' is set to True # Properly handles multiple outputs when 'multiple' is set to True
def test_handles_multiple_outputs(self): def test_handles_multiple_outputs(self):
# Import internal organization modules, packages, and libraries
from langflow.helpers.base_model import build_model_from_schema
from langflow.inputs.inputs import TableInput
# Setup # Setup
component = StructuredOutputComponent() component = StructuredOutputComponent()
schema = [ schema = [
@ -261,5 +254,5 @@ class TestStructuredOutputComponent:
multiple=False, multiple=False,
) )
with pytest.raises(TypeError, match="Language model does not support structured output."): with pytest.raises(TypeError, match=re.escape("Language model does not support structured output.")):
component.build_structured_output() component.build_structured_output()

View file

@ -44,7 +44,7 @@ def test_operations(sample_dataframe, operation, expected_columns, expected_valu
component.new_column_name = "Z" component.new_column_name = "Z"
elif operation == "Select Columns": elif operation == "Select Columns":
component.columns_to_select = ["A", "C"] component.columns_to_select = ["A", "C"]
elif operation in ("Head", "Tail"): elif operation in {"Head", "Tail"}:
component.num_rows = 1 component.num_rows = 1
elif operation == "Replace Value": elif operation == "Replace Value":
component.column_name = "C" component.column_name = "C"

View file

@ -1,3 +1,5 @@
import re
import pytest import pytest
from langflow.components.processing import CreateDataComponent from langflow.components.processing import CreateDataComponent
from langflow.schema import Data from langflow.schema import Data
@ -48,7 +50,7 @@ def test_update_build_config_exceed_limit(create_data_component):
"value": False, "value": False,
}, },
} }
with pytest.raises(ValueError, match="Number of fields cannot exceed 15."): with pytest.raises(ValueError, match=re.escape("Number of fields cannot exceed 15.")):
create_data_component.update_build_config(build_config, 16, "number_of_fields") create_data_component.update_build_config(build_config, 16, "number_of_fields")

View file

@ -1,3 +1,5 @@
import re
import pytest import pytest
from langflow.components.processing import UpdateDataComponent from langflow.components.processing import UpdateDataComponent
from langflow.schema import Data from langflow.schema import Data
@ -48,7 +50,7 @@ def test_update_build_config_exceed_limit(update_data_component):
"value": False, "value": False,
}, },
} }
with pytest.raises(ValueError, match="Number of fields cannot exceed 15."): with pytest.raises(ValueError, match=re.escape("Number of fields cannot exceed 15.")):
update_data_component.update_build_config(build_config, 16, "number_of_fields") update_data_component.update_build_config(build_config, 16, "number_of_fields")

View file

@ -14,11 +14,6 @@ from langflow.schema.properties import Properties, Source
from langflow.template.field.base import Output from langflow.template.field.base import Output
async def create_event_queue():
"""Create a queue for testing events."""
return asyncio.Queue()
def blocking_cb(manager, event_type, data): def blocking_cb(manager, event_type, data):
time.sleep(0.01) time.sleep(0.01)
manager.send_event(event_type=event_type, data=data) manager.send_event(event_type=event_type, data=data)
@ -43,7 +38,7 @@ class ComponentForTesting(Component):
async def test_component_message_sending(): async def test_component_message_sending():
"""Test component's message sending functionality.""" """Test component's message sending functionality."""
# Create event queue and manager # Create event queue and manager
queue = await create_event_queue() queue = asyncio.Queue()
event_manager = EventManager(queue) event_manager = EventManager(queue)
event_manager.register_event("on_message", "message", callback=blocking_cb) event_manager.register_event("on_message", "message", callback=blocking_cb)
@ -75,7 +70,7 @@ async def test_component_message_sending():
async def test_component_tool_output(): async def test_component_tool_output():
"""Test component's tool output functionality.""" """Test component's tool output functionality."""
# Create event queue and manager # Create event queue and manager
queue = await create_event_queue() queue = asyncio.Queue()
event_manager = EventManager(queue) event_manager = EventManager(queue)
# Create component # Create component
@ -110,7 +105,7 @@ async def test_component_tool_output():
async def test_component_error_handling(): async def test_component_error_handling():
"""Test component's error handling.""" """Test component's error handling."""
# Create event queue and manager # Create event queue and manager
queue = await create_event_queue() queue = asyncio.Queue()
event_manager = EventManager(queue) event_manager = EventManager(queue)
# Create component # Create component
@ -141,7 +136,7 @@ async def test_component_error_handling():
async def test_component_build_results(): async def test_component_build_results():
"""Test component's build_results functionality.""" """Test component's build_results functionality."""
# Create event queue and manager # Create event queue and manager
queue = await create_event_queue() queue = asyncio.Queue()
event_manager = EventManager(queue) event_manager = EventManager(queue)
# Create component # Create component
@ -173,7 +168,7 @@ async def test_component_build_results():
async def test_component_logging(): async def test_component_logging():
"""Test component's logging functionality.""" """Test component's logging functionality."""
# Create event queue and manager # Create event queue and manager
queue = await create_event_queue() queue = asyncio.Queue()
event_manager = EventManager(queue) event_manager = EventManager(queue)
# Create component # Create component
@ -207,7 +202,7 @@ async def test_component_logging():
@pytest.mark.usefixtures("client") @pytest.mark.usefixtures("client")
async def test_component_streaming_message(): async def test_component_streaming_message():
"""Test component's streaming message functionality.""" """Test component's streaming message functionality."""
queue = await create_event_queue() queue = asyncio.Queue()
event_manager = EventManager(queue) event_manager = EventManager(queue)
event_manager.register_event("on_token", "token", blocking_cb) event_manager.register_event("on_token", "token", blocking_cb)

View file

@ -40,7 +40,7 @@ class TestEventManager:
def test_accessing_non_registered_event_callback_with_recommended_fix(self): def test_accessing_non_registered_event_callback_with_recommended_fix(self):
queue = asyncio.Queue() queue = asyncio.Queue()
manager = EventManager(queue) manager = EventManager(queue)
result = manager.__getattr__("non_registered_event") result = manager.non_registered_event
assert result == manager.noop assert result == manager.noop
# Accessing a registered event callback via __getattr__ # Accessing a registered event callback via __getattr__
@ -130,8 +130,6 @@ class TestEventManager:
assert len(manager.events) == 1000 assert len(manager.events) == 1000
# Verifying the uniqueness of event IDs for each event triggered using await with asyncio decorator # Verifying the uniqueness of event IDs for each event triggered using await with asyncio decorator
import pytest
async def test_event_id_uniqueness_with_await(self): async def test_event_id_uniqueness_with_await(self):
queue = asyncio.Queue() queue = asyncio.Queue()
manager = EventManager(queue) manager = EventManager(queue)

View file

@ -1,11 +1,10 @@
from unittest.mock import Mock, patch from unittest.mock import Mock, patch
from langflow.exceptions.api import APIException, ExceptionBody
from langflow.services.database.models.flow.model import Flow from langflow.services.database.models.flow.model import Flow
def test_api_exception(): def test_api_exception():
from langflow.exceptions.api import APIException, ExceptionBody
mock_exception = Exception("Test exception") mock_exception = Exception("Test exception")
mock_flow = Mock(spec=Flow) mock_flow = Mock(spec=Flow)
mock_outdated_components = ["component1", "component2"] mock_outdated_components = ["component1", "component2"]
@ -45,8 +44,6 @@ def test_api_exception():
def test_api_exception_no_flow(): def test_api_exception_no_flow():
from langflow.exceptions.api import APIException, ExceptionBody
# Mock data # Mock data
mock_exception = Exception("Test exception") mock_exception = Exception("Test exception")

View file

@ -1,3 +1,5 @@
import re
import pytest import pytest
from langflow.components.inputs import ChatInput from langflow.components.inputs import ChatInput
from langflow.components.models import OpenAIModelComponent from langflow.components.models import OpenAIModelComponent
@ -25,5 +27,7 @@ Answer:
chat_output = ChatOutput() chat_output = ChatOutput()
chat_output.set(input_value=openai_component.text_response) chat_output.set(input_value=openai_component.text_response)
with pytest.raises(ValueError, match="Component OpenAI field 'input_values' might not be a valid input."): with pytest.raises(
ValueError, match=re.escape("Component OpenAI field 'input_values' might not be a valid input.")
):
Graph(start=chat_input, end=chat_output) Graph(start=chat_input, end=chat_output)

View file

@ -20,7 +20,7 @@ async def test_graph_not_prepared():
await graph.astep() await graph.astep()
async def test_graph(caplog: pytest.LogCaptureFixture): def test_graph(caplog: pytest.LogCaptureFixture):
chat_input = ChatInput() chat_input = ChatInput()
chat_output = ChatOutput() chat_output = ChatOutput()
graph = Graph() graph = Graph()

View file

@ -2,14 +2,14 @@ from typing import TYPE_CHECKING, Literal
import pytest import pytest
from langflow.components.inputs import ChatInput from langflow.components.inputs import ChatInput
from langflow.inputs.inputs import DropdownInput, FileInput, IntInput, NestedDictInput, StrInput
from langflow.io.schema import create_input_schema
if TYPE_CHECKING: if TYPE_CHECKING:
from pydantic.fields import FieldInfo from pydantic.fields import FieldInfo
def test_create_input_schema(): def test_create_input_schema():
from langflow.io.schema import create_input_schema
schema = create_input_schema(ChatInput.inputs) schema = create_input_schema(ChatInput.inputs)
assert schema.__name__ == "InputSchema" assert schema.__name__ == "InputSchema"
@ -17,9 +17,6 @@ def test_create_input_schema():
class TestCreateInputSchema: class TestCreateInputSchema:
# Single input type is converted to list and processed correctly # Single input type is converted to list and processed correctly
def test_single_input_type_conversion(self): def test_single_input_type_conversion(self):
from langflow.inputs.inputs import StrInput
from langflow.io.schema import create_input_schema
input_instance = StrInput(name="test_field") input_instance = StrInput(name="test_field")
schema = create_input_schema([input_instance]) schema = create_input_schema([input_instance])
assert schema.__name__ == "InputSchema" assert schema.__name__ == "InputSchema"
@ -27,9 +24,6 @@ class TestCreateInputSchema:
# Multiple input types are processed and included in the schema # Multiple input types are processed and included in the schema
def test_multiple_input_types(self): def test_multiple_input_types(self):
from langflow.inputs.inputs import IntInput, StrInput
from langflow.io.schema import create_input_schema
inputs = [StrInput(name="str_field"), IntInput(name="int_field")] inputs = [StrInput(name="str_field"), IntInput(name="int_field")]
schema = create_input_schema(inputs) schema = create_input_schema(inputs)
assert schema.__name__ == "InputSchema" assert schema.__name__ == "InputSchema"
@ -38,9 +32,6 @@ class TestCreateInputSchema:
# Fields are correctly created with appropriate types and attributes # Fields are correctly created with appropriate types and attributes
def test_fields_creation_with_correct_types_and_attributes(self): def test_fields_creation_with_correct_types_and_attributes(self):
from langflow.inputs.inputs import StrInput
from langflow.io.schema import create_input_schema
input_instance = StrInput(name="test_field", info="Test Info", required=True) input_instance = StrInput(name="test_field", info="Test Info", required=True)
schema = create_input_schema([input_instance]) schema = create_input_schema([input_instance])
field_info = schema.model_fields["test_field"] field_info = schema.model_fields["test_field"]
@ -49,18 +40,12 @@ class TestCreateInputSchema:
# Schema model is created and returned successfully # Schema model is created and returned successfully
def test_schema_model_creation(self): def test_schema_model_creation(self):
from langflow.inputs.inputs import StrInput
from langflow.io.schema import create_input_schema
input_instance = StrInput(name="test_field") input_instance = StrInput(name="test_field")
schema = create_input_schema([input_instance]) schema = create_input_schema([input_instance])
assert schema.__name__ == "InputSchema" assert schema.__name__ == "InputSchema"
# Default values are correctly assigned to fields # Default values are correctly assigned to fields
def test_default_values_assignment(self): def test_default_values_assignment(self):
from langflow.inputs.inputs import StrInput
from langflow.io.schema import create_input_schema
input_instance = StrInput(name="test_field", value="default_value") input_instance = StrInput(name="test_field", value="default_value")
schema = create_input_schema([input_instance]) schema = create_input_schema([input_instance])
field_info = schema.model_fields["test_field"] field_info = schema.model_fields["test_field"]
@ -68,16 +53,11 @@ class TestCreateInputSchema:
# Empty list of inputs is handled without errors # Empty list of inputs is handled without errors
def test_empty_list_of_inputs(self): def test_empty_list_of_inputs(self):
from langflow.io.schema import create_input_schema
schema = create_input_schema([]) schema = create_input_schema([])
assert schema.__name__ == "InputSchema" assert schema.__name__ == "InputSchema"
# Input with missing optional attributes (e.g., display_name, info) is processed correctly # Input with missing optional attributes (e.g., display_name, info) is processed correctly
def test_missing_optional_attributes(self): def test_missing_optional_attributes(self):
from langflow.inputs.inputs import StrInput
from langflow.io.schema import create_input_schema
input_instance = StrInput(name="test_field") input_instance = StrInput(name="test_field")
schema = create_input_schema([input_instance]) schema = create_input_schema([input_instance])
field_info = schema.model_fields["test_field"] field_info = schema.model_fields["test_field"]
@ -86,9 +66,6 @@ class TestCreateInputSchema:
# Input with is_list attribute set to True is processed correctly # Input with is_list attribute set to True is processed correctly
def test_is_list_attribute_processing(self): def test_is_list_attribute_processing(self):
from langflow.inputs.inputs import StrInput
from langflow.io.schema import create_input_schema
input_instance = StrInput(name="test_field", is_list=True) input_instance = StrInput(name="test_field", is_list=True)
schema = create_input_schema([input_instance]) schema = create_input_schema([input_instance])
field_info: FieldInfo = schema.model_fields["test_field"] field_info: FieldInfo = schema.model_fields["test_field"]
@ -96,9 +73,6 @@ class TestCreateInputSchema:
# Input with options attribute is processed correctly # Input with options attribute is processed correctly
def test_options_attribute_processing(self): def test_options_attribute_processing(self):
from langflow.inputs.inputs import DropdownInput
from langflow.io.schema import create_input_schema
input_instance = DropdownInput(name="test_field", options=["option1", "option2"]) input_instance = DropdownInput(name="test_field", options=["option1", "option2"])
schema = create_input_schema([input_instance]) schema = create_input_schema([input_instance])
field_info = schema.model_fields["test_field"] field_info = schema.model_fields["test_field"]
@ -106,9 +80,6 @@ class TestCreateInputSchema:
# Non-standard field types are handled correctly # Non-standard field types are handled correctly
def test_non_standard_field_types_handling(self): def test_non_standard_field_types_handling(self):
from langflow.inputs.inputs import FileInput
from langflow.io.schema import create_input_schema
input_instance = FileInput(name="file_field") input_instance = FileInput(name="file_field")
schema = create_input_schema([input_instance]) schema = create_input_schema([input_instance])
field_info = schema.model_fields["file_field"] field_info = schema.model_fields["file_field"]
@ -116,9 +87,6 @@ class TestCreateInputSchema:
# Inputs with mixed required and optional fields are processed correctly # Inputs with mixed required and optional fields are processed correctly
def test_mixed_required_optional_fields_processing(self): def test_mixed_required_optional_fields_processing(self):
from langflow.inputs.inputs import IntInput, StrInput
from langflow.io.schema import create_input_schema
inputs = [ inputs = [
StrInput(name="required_field", required=True), StrInput(name="required_field", required=True),
IntInput(name="optional_field", required=False), IntInput(name="optional_field", required=False),
@ -132,9 +100,6 @@ class TestCreateInputSchema:
# Inputs with complex nested structures are handled correctly # Inputs with complex nested structures are handled correctly
def test_complex_nested_structures_handling(self): def test_complex_nested_structures_handling(self):
from langflow.inputs.inputs import NestedDictInput
from langflow.io.schema import create_input_schema
nested_input = NestedDictInput(name="nested_field", value={"key": "value"}) nested_input = NestedDictInput(name="nested_field", value={"key": "value"})
schema = create_input_schema([nested_input]) schema = create_input_schema([nested_input])
@ -145,9 +110,6 @@ class TestCreateInputSchema:
# Creating a schema from a single input type # Creating a schema from a single input type
def test_single_input_type_replica(self): def test_single_input_type_replica(self):
from langflow.inputs.inputs import StrInput
from langflow.io.schema import create_input_schema
input_instance = StrInput(name="test_field") input_instance = StrInput(name="test_field")
schema = create_input_schema([input_instance]) schema = create_input_schema([input_instance])
assert schema.__name__ == "InputSchema" assert schema.__name__ == "InputSchema"
@ -155,18 +117,12 @@ class TestCreateInputSchema:
# Creating a schema from a list of input types # Creating a schema from a list of input types
def test_passing_input_type_directly(self): def test_passing_input_type_directly(self):
from langflow.inputs.inputs import IntInput, StrInput
from langflow.io.schema import create_input_schema
inputs = StrInput(name="str_field"), IntInput(name="int_field") inputs = StrInput(name="str_field"), IntInput(name="int_field")
with pytest.raises(TypeError): with pytest.raises(TypeError):
create_input_schema(inputs) create_input_schema(inputs)
# Handling input types with options correctly # Handling input types with options correctly
def test_options_handling(self): def test_options_handling(self):
from langflow.inputs.inputs import DropdownInput
from langflow.io.schema import create_input_schema
input_instance = DropdownInput(name="test_field", options=["option1", "option2"]) input_instance = DropdownInput(name="test_field", options=["option1", "option2"])
schema = create_input_schema([input_instance]) schema = create_input_schema([input_instance])
field_info = schema.model_fields["test_field"] field_info = schema.model_fields["test_field"]
@ -174,9 +130,6 @@ class TestCreateInputSchema:
# Handling input types with is_list attribute correctly # Handling input types with is_list attribute correctly
def test_is_list_handling(self): def test_is_list_handling(self):
from langflow.inputs.inputs import StrInput
from langflow.io.schema import create_input_schema
input_instance = StrInput(name="test_field", is_list=True) input_instance = StrInput(name="test_field", is_list=True)
schema = create_input_schema([input_instance]) schema = create_input_schema([input_instance])
field_info = schema.model_fields["test_field"] field_info = schema.model_fields["test_field"]
@ -184,9 +137,6 @@ class TestCreateInputSchema:
# Converting FieldTypes to corresponding Python types # Converting FieldTypes to corresponding Python types
def test_field_types_conversion(self): def test_field_types_conversion(self):
from langflow.inputs.inputs import IntInput
from langflow.io.schema import create_input_schema
input_instance = IntInput(name="int_field") input_instance = IntInput(name="int_field")
schema = create_input_schema([input_instance]) schema = create_input_schema([input_instance])
field_info = schema.model_fields["int_field"] field_info = schema.model_fields["int_field"]
@ -194,9 +144,6 @@ class TestCreateInputSchema:
# Setting default values for non-required fields # Setting default values for non-required fields
def test_default_values_for_non_required_fields(self): def test_default_values_for_non_required_fields(self):
from langflow.inputs.inputs import StrInput
from langflow.io.schema import create_input_schema
input_instance = StrInput(name="test_field", value="default_value") input_instance = StrInput(name="test_field", value="default_value")
schema = create_input_schema([input_instance]) schema = create_input_schema([input_instance])
field_info = schema.model_fields["test_field"] field_info = schema.model_fields["test_field"]
@ -204,9 +151,6 @@ class TestCreateInputSchema:
# Handling input types with missing attributes # Handling input types with missing attributes
def test_missing_attributes_handling(self): def test_missing_attributes_handling(self):
from langflow.inputs.inputs import StrInput
from langflow.io.schema import create_input_schema
input_instance = StrInput(name="test_field") input_instance = StrInput(name="test_field")
schema = create_input_schema([input_instance]) schema = create_input_schema([input_instance])
field_info = schema.model_fields["test_field"] field_info = schema.model_fields["test_field"]
@ -217,9 +161,6 @@ class TestCreateInputSchema:
# Handling input types with None as default value # Handling input types with None as default value
def test_none_default_value_handling(self): def test_none_default_value_handling(self):
from langflow.inputs.inputs import StrInput
from langflow.io.schema import create_input_schema
input_instance = StrInput(name="test_field", value=None) input_instance = StrInput(name="test_field", value=None)
schema = create_input_schema([input_instance]) schema = create_input_schema([input_instance])
field_info = schema.model_fields["test_field"] field_info = schema.model_fields["test_field"]
@ -227,9 +168,6 @@ class TestCreateInputSchema:
# Handling input types with special characters in names # Handling input types with special characters in names
def test_special_characters_in_names_handling(self): def test_special_characters_in_names_handling(self):
from langflow.inputs.inputs import StrInput
from langflow.io.schema import create_input_schema
input_instance = StrInput(name="test@field#name") input_instance = StrInput(name="test@field#name")
schema = create_input_schema([input_instance]) schema = create_input_schema([input_instance])
assert "test@field#name" in schema.model_fields assert "test@field#name" in schema.model_fields

View file

@ -1,3 +1,5 @@
import base64
import pytest import pytest
from langchain_core.messages import AIMessage, HumanMessage from langchain_core.messages import AIMessage, HumanMessage
from langflow.schema.data import Data from langflow.schema.data import Data
@ -9,8 +11,6 @@ def sample_image(tmp_path):
"""Create a sample image file for testing.""" """Create a sample image file for testing."""
image_path = tmp_path / "test_image.png" image_path = tmp_path / "test_image.png"
# Create a small black 1x1 pixel PNG file # Create a small black 1x1 pixel PNG file
import base64
image_content = base64.b64decode( image_content = base64.b64decode(
"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAACklEQVR4nGMAAQAABQABDQottAAAAABJRU5ErkJggg==" "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAACklEQVR4nGMAAQAABQABDQottAAAAABJRU5ErkJggg=="
) )

View file

@ -1,3 +1,4 @@
import base64
import shutil import shutil
from datetime import datetime, timezone from datetime import datetime, timezone
from pathlib import Path from pathlib import Path
@ -29,8 +30,6 @@ def sample_image(langflow_cache_dir):
# Create the image in the flow directory # Create the image in the flow directory
image_path = flow_dir / "test_image.png" image_path = flow_dir / "test_image.png"
# Create a small black 1x1 pixel PNG file # Create a small black 1x1 pixel PNG file
import base64
image_content = base64.b64decode( image_content = base64.b64decode(
"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAACklEQVR4nGMAAQAABQABDQottAAAAABJRU5ErkJggg==" "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAACklEQVR4nGMAAQAABQABDQottAAAAABJRU5ErkJggg=="
) )

View file

@ -1,3 +1,5 @@
import copy
import pytest import pytest
from langchain_core.documents import Document from langchain_core.documents import Document
from langflow.schema import Data from langflow.schema import Data
@ -62,8 +64,6 @@ def test_custom_attribute_get_set_del():
def test_deep_copy(): def test_deep_copy():
import copy
record1 = Data(data={"text": "Hello", "number": 10}) record1 = Data(data={"text": "Hello", "number": 10})
record2 = copy.deepcopy(record1) record2 = copy.deepcopy(record1)
assert record2.text == "Hello" assert record2.text == "Hello"

View file

@ -13,6 +13,7 @@ from langflow.services.database.models.flow import Flow, FlowCreate, FlowUpdate
from langflow.services.database.models.folder.model import FolderCreate from langflow.services.database.models.folder.model import FolderCreate
from langflow.services.database.utils import session_getter from langflow.services.database.utils import session_getter
from langflow.services.deps import get_db_service from langflow.services.deps import get_db_service
from sqlalchemy import text
@pytest.fixture(scope="module") @pytest.fixture(scope="module")
@ -619,8 +620,6 @@ async def test_sqlite_pragmas():
db_service = get_db_service() db_service = get_db_service()
async with db_service.with_session() as session: async with db_service.with_session() as session:
from sqlalchemy import text
assert (await session.exec(text("PRAGMA journal_mode;"))).scalar() == "wal" assert (await session.exec(text("PRAGMA journal_mode;"))).scalar() == "wal"
assert (await session.exec(text("PRAGMA synchronous;"))).scalar() == 1 assert (await session.exec(text("PRAGMA synchronous;"))).scalar() == 1

View file

@ -64,7 +64,7 @@ class TestInput:
# Empty lists and edge cases # Empty lists and edge cases
assert set(post_process_type(list)) == {list} assert set(post_process_type(list)) == {list}
assert set(post_process_type(Union[int, None])) == {int, NoneType} # noqa: UP007 assert set(post_process_type(Union[int, None])) == {int, NoneType} # noqa: UP007
assert set(post_process_type(Union[None, list[None]])) == {None, NoneType} # noqa: UP007 assert set(post_process_type(Union[list[None], None])) == {None, NoneType} # noqa: UP007
# Handling complex nested structures # Handling complex nested structures
assert set(post_process_type(Union[SequenceABC[int | str], list[float]])) == {int, str, float} # noqa: UP007 assert set(post_process_type(Union[SequenceABC[int | str], list[float]])) == {int, str, float} # noqa: UP007

View file

@ -1,3 +1,4 @@
import re
import threading import threading
from concurrent.futures import ThreadPoolExecutor, as_completed from concurrent.futures import ThreadPoolExecutor, as_completed
@ -48,7 +49,7 @@ def test_increment_counter_empty_label(opentelemetry_instance):
def test_increment_counter_missing_mandatory_label(opentelemetry_instance): def test_increment_counter_missing_mandatory_label(opentelemetry_instance):
with pytest.raises(ValueError, match="Missing required labels: {'flow_id'}"): with pytest.raises(ValueError, match=re.escape("Missing required labels: {'flow_id'}")):
opentelemetry_instance.increment_counter(metric_name="num_files_uploaded", value=5, labels={"service": "one"}) opentelemetry_instance.increment_counter(metric_name="num_files_uploaded", value=5, labels={"service": "one"})

View file

@ -16,51 +16,57 @@ def sample_image(tmp_path):
return image_path return image_path
class TestImageUtils: def test_convert_image_to_base64_success(sample_image):
def test_convert_image_to_base64_success(self, sample_image): """Test successful conversion of image to base64."""
"""Test successful conversion of image to base64.""" base64_str = convert_image_to_base64(sample_image)
base64_str = convert_image_to_base64(sample_image) assert isinstance(base64_str, str)
assert isinstance(base64_str, str) # Verify it's valid base64
# Verify it's valid base64 assert base64.b64decode(base64_str)
assert base64.b64decode(base64_str)
def test_convert_image_to_base64_empty_path(self):
"""Test conversion with empty path."""
with pytest.raises(ValueError, match="Image path cannot be empty"):
convert_image_to_base64("")
def test_convert_image_to_base64_nonexistent_file(self): def test_convert_image_to_base64_empty_path():
"""Test conversion with non-existent file.""" """Test conversion with empty path."""
with pytest.raises(FileNotFoundError, match="Image file not found"): with pytest.raises(ValueError, match="Image path cannot be empty"):
convert_image_to_base64("nonexistent.png") convert_image_to_base64("")
def test_convert_image_to_base64_directory(self, tmp_path):
"""Test conversion with directory path instead of file."""
with pytest.raises(ValueError, match="Path is not a file"):
convert_image_to_base64(tmp_path)
def test_create_data_url_success(self, sample_image): def test_convert_image_to_base64_nonexistent_file():
"""Test successful creation of data URL.""" """Test conversion with non-existent file."""
data_url = create_data_url(sample_image) with pytest.raises(FileNotFoundError, match="Image file not found"):
assert data_url.startswith("data:image/png;base64,") convert_image_to_base64("nonexistent.png")
# Verify the base64 part is valid
base64_part = data_url.split(",")[1]
assert base64.b64decode(base64_part)
def test_create_data_url_with_custom_mime(self, sample_image):
"""Test creation of data URL with custom MIME type."""
custom_mime = "image/custom"
data_url = create_data_url(sample_image, mime_type=custom_mime)
assert data_url.startswith(f"data:{custom_mime};base64,")
def test_create_data_url_invalid_file(self): def test_convert_image_to_base64_directory(tmp_path):
"""Test creation of data URL with invalid file.""" """Test conversion with directory path instead of file."""
with pytest.raises(FileNotFoundError): with pytest.raises(ValueError, match="Path is not a file"):
create_data_url("nonexistent.jpg") convert_image_to_base64(tmp_path)
def test_create_data_url_unrecognized_extension(self, tmp_path):
"""Test creation of data URL with unrecognized file extension.""" def test_create_data_url_success(sample_image):
invalid_file = tmp_path / "test.unknown" """Test successful creation of data URL."""
invalid_file.touch() data_url = create_data_url(sample_image)
with pytest.raises(ValueError, match="Could not determine MIME type"): assert data_url.startswith("data:image/png;base64,")
create_data_url(invalid_file) # Verify the base64 part is valid
base64_part = data_url.split(",")[1]
assert base64.b64decode(base64_part)
def test_create_data_url_with_custom_mime(sample_image):
"""Test creation of data URL with custom MIME type."""
custom_mime = "image/custom"
data_url = create_data_url(sample_image, mime_type=custom_mime)
assert data_url.startswith(f"data:{custom_mime};base64,")
def test_create_data_url_invalid_file():
"""Test creation of data URL with invalid file."""
with pytest.raises(FileNotFoundError):
create_data_url("nonexistent.jpg")
def test_create_data_url_unrecognized_extension(tmp_path):
"""Test creation of data URL with unrecognized file extension."""
invalid_file = tmp_path / "test.unknown"
invalid_file.touch()
with pytest.raises(ValueError, match="Could not determine MIME type"):
create_data_url(invalid_file)

View file

@ -1,6 +1,7 @@
import math import math
import pytest import pytest
from langflow.utils.constants import MAX_TEXT_LENGTH
from langflow.utils.util_strings import truncate_long_strings from langflow.utils.util_strings import truncate_long_strings
@ -48,8 +49,6 @@ def test_truncate_long_strings_negative_max_length():
# Test for None max_length (should use default MAX_TEXT_LENGTH) # Test for None max_length (should use default MAX_TEXT_LENGTH)
def test_truncate_long_strings_none_max_length(): def test_truncate_long_strings_none_max_length():
from langflow.utils.constants import MAX_TEXT_LENGTH
long_string = "a" * (MAX_TEXT_LENGTH + 10) long_string = "a" * (MAX_TEXT_LENGTH + 10)
result = truncate_long_strings(long_string, None) result = truncate_long_strings(long_string, None)
assert len(result) == MAX_TEXT_LENGTH + 3 # +3 for "..." assert len(result) == MAX_TEXT_LENGTH + 3 # +3 for "..."

122
uv.lock generated
View file

@ -532,7 +532,7 @@ name = "blessed"
version = "1.20.0" version = "1.20.0"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "jinxed", marker = "platform_system == 'Windows'" }, { name = "jinxed", marker = "sys_platform == 'win32'" },
{ name = "six" }, { name = "six" },
{ name = "wcwidth" }, { name = "wcwidth" },
] ]
@ -962,7 +962,7 @@ name = "click"
version = "8.1.7" version = "8.1.7"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "colorama", marker = "platform_system == 'Windows'" }, { name = "colorama", marker = "sys_platform == 'win32'" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/96/d3/f04c7bfcf5c1862a2a5b845c6b2b360488cf47af55dfa79c98f6a6bf98b5/click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de", size = 336121 } sdist = { url = "https://files.pythonhosted.org/packages/96/d3/f04c7bfcf5c1862a2a5b845c6b2b360488cf47af55dfa79c98f6a6bf98b5/click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de", size = 336121 }
wheels = [ wheels = [
@ -3143,7 +3143,7 @@ name = "ipykernel"
version = "6.29.5" version = "6.29.5"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "appnope", marker = "platform_system == 'Darwin'" }, { name = "appnope", marker = "sys_platform == 'darwin'" },
{ name = "comm" }, { name = "comm" },
{ name = "debugpy" }, { name = "debugpy" },
{ name = "ipython" }, { name = "ipython" },
@ -3234,7 +3234,7 @@ name = "jinxed"
version = "1.3.0" version = "1.3.0"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "ansicon", marker = "platform_system == 'Windows'" }, { name = "ansicon", marker = "sys_platform == 'win32'" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/20/d0/59b2b80e7a52d255f9e0ad040d2e826342d05580c4b1d7d7747cfb8db731/jinxed-1.3.0.tar.gz", hash = "sha256:1593124b18a41b7a3da3b078471442e51dbad3d77b4d4f2b0c26ab6f7d660dbf", size = 80981 } sdist = { url = "https://files.pythonhosted.org/packages/20/d0/59b2b80e7a52d255f9e0ad040d2e826342d05580c4b1d7d7747cfb8db731/jinxed-1.3.0.tar.gz", hash = "sha256:1593124b18a41b7a3da3b078471442e51dbad3d77b4d4f2b0c26ab6f7d660dbf", size = 80981 }
wheels = [ wheels = [
@ -4198,7 +4198,7 @@ dev = [
{ name = "pytest-xdist", specifier = ">=3.6.0" }, { name = "pytest-xdist", specifier = ">=3.6.0" },
{ name = "requests", specifier = ">=2.32.0" }, { name = "requests", specifier = ">=2.32.0" },
{ name = "respx", specifier = ">=0.21.1" }, { name = "respx", specifier = ">=0.21.1" },
{ name = "ruff", specifier = ">=0.8.2,<0.9.0" }, { name = "ruff", specifier = ">=0.8.4,<0.9.0" },
{ name = "types-aiofiles", specifier = ">=24.1.0.20240626" }, { name = "types-aiofiles", specifier = ">=24.1.0.20240626" },
{ name = "types-google-cloud-ndb", specifier = ">=2.2.0.0" }, { name = "types-google-cloud-ndb", specifier = ">=2.2.0.0" },
{ name = "types-markdown", specifier = ">=3.7.0.20240822" }, { name = "types-markdown", specifier = ">=3.7.0.20240822" },
@ -5126,36 +5126,36 @@ wheels = [
[[package]] [[package]]
name = "mypy" name = "mypy"
version = "1.13.0" version = "1.14.0"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "mypy-extensions" }, { name = "mypy-extensions" },
{ name = "tomli", marker = "python_full_version < '3.11'" }, { name = "tomli", marker = "python_full_version < '3.11'" },
{ name = "typing-extensions" }, { name = "typing-extensions" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/e8/21/7e9e523537991d145ab8a0a2fd98548d67646dc2aaaf6091c31ad883e7c1/mypy-1.13.0.tar.gz", hash = "sha256:0291a61b6fbf3e6673e3405cfcc0e7650bebc7939659fdca2702958038bd835e", size = 3152532 } sdist = { url = "https://files.pythonhosted.org/packages/8c/7b/08046ef9330735f536a09a2e31b00f42bccdb2795dcd979636ba43bb2d63/mypy-1.14.0.tar.gz", hash = "sha256:822dbd184d4a9804df5a7d5335a68cf7662930e70b8c1bc976645d1509f9a9d6", size = 3215684 }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/5e/8c/206de95a27722b5b5a8c85ba3100467bd86299d92a4f71c6b9aa448bfa2f/mypy-1.13.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6607e0f1dd1fb7f0aca14d936d13fd19eba5e17e1cd2a14f808fa5f8f6d8f60a", size = 11020731 }, { url = "https://files.pythonhosted.org/packages/ef/97/f00ded038482230e0beaaa08f9c5483a54530b362ad1b0d752d5d2b2f211/mypy-1.14.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e971c1c667007f9f2b397ffa80fa8e1e0adccff336e5e77e74cb5f22868bee87", size = 11207956 },
{ url = "https://files.pythonhosted.org/packages/ab/bb/b31695a29eea76b1569fd28b4ab141a1adc9842edde080d1e8e1776862c7/mypy-1.13.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8a21be69bd26fa81b1f80a61ee7ab05b076c674d9b18fb56239d72e21d9f4c80", size = 10184276 }, { url = "https://files.pythonhosted.org/packages/68/67/8b4db0da19c9e3fa6264e948f1c135ab4dd45bede1809f4fdb613dc119f6/mypy-1.14.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e86aaeaa3221a278c66d3d673b297232947d873773d61ca3ee0e28b2ff027179", size = 10363681 },
{ url = "https://files.pythonhosted.org/packages/a5/2d/4a23849729bb27934a0e079c9c1aad912167d875c7b070382a408d459651/mypy-1.13.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7b2353a44d2179846a096e25691d54d59904559f4232519d420d64da6828a3a7", size = 12587706 }, { url = "https://files.pythonhosted.org/packages/f5/00/56b1619ff1f3fcad2d411eccda60d74d20e73bda39c218d5ad2769980682/mypy-1.14.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1628c5c3ce823d296e41e2984ff88c5861499041cb416a8809615d0c1f41740e", size = 12832976 },
{ url = "https://files.pythonhosted.org/packages/5c/c3/d318e38ada50255e22e23353a469c791379825240e71b0ad03e76ca07ae6/mypy-1.13.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0730d1c6a2739d4511dc4253f8274cdd140c55c32dfb0a4cf8b7a43f40abfa6f", size = 13105586 }, { url = "https://files.pythonhosted.org/packages/e7/8b/9247838774b0bd865f190cc221822212091317f16310305ef924d9772532/mypy-1.14.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7fadb29b77fc14a0dd81304ed73c828c3e5cde0016c7e668a86a3e0dfc9f3af3", size = 13013704 },
{ url = "https://files.pythonhosted.org/packages/4a/25/3918bc64952370c3dbdbd8c82c363804678127815febd2925b7273d9482c/mypy-1.13.0-cp310-cp310-win_amd64.whl", hash = "sha256:c5fc54dbb712ff5e5a0fca797e6e0aa25726c7e72c6a5850cfd2adbc1eb0a372", size = 9632318 }, { url = "https://files.pythonhosted.org/packages/b2/69/0c0868a6f3d9761d2f704d1fb6ef84d75998c27d342738a8b20f109a411f/mypy-1.14.0-cp310-cp310-win_amd64.whl", hash = "sha256:3fa76988dc760da377c1e5069200a50d9eaaccf34f4ea18428a3337034ab5a44", size = 9782230 },
{ url = "https://files.pythonhosted.org/packages/d0/19/de0822609e5b93d02579075248c7aa6ceaddcea92f00bf4ea8e4c22e3598/mypy-1.13.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:581665e6f3a8a9078f28d5502f4c334c0c8d802ef55ea0e7276a6e409bc0d82d", size = 10939027 }, { url = "https://files.pythonhosted.org/packages/34/c1/b9dd3e955953aec1c728992545b7877c9f6fa742a623ce4c200da0f62540/mypy-1.14.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6e73c8a154eed31db3445fe28f63ad2d97b674b911c00191416cf7f6459fd49a", size = 11121032 },
{ url = "https://files.pythonhosted.org/packages/c8/71/6950fcc6ca84179137e4cbf7cf41e6b68b4a339a1f5d3e954f8c34e02d66/mypy-1.13.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3ddb5b9bf82e05cc9a627e84707b528e5c7caaa1c55c69e175abb15a761cec2d", size = 10108699 }, { url = "https://files.pythonhosted.org/packages/ee/96/c52d5d516819ab95bf41f4a1ada828a3decc302f8c152ff4fc5feb0e4529/mypy-1.14.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:273e70fcb2e38c5405a188425aa60b984ffdcef65d6c746ea5813024b68c73dc", size = 10286294 },
{ url = "https://files.pythonhosted.org/packages/26/50/29d3e7dd166e74dc13d46050b23f7d6d7533acf48f5217663a3719db024e/mypy-1.13.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:20c7ee0bc0d5a9595c46f38beb04201f2620065a93755704e141fcac9f59db2b", size = 12506263 }, { url = "https://files.pythonhosted.org/packages/69/2c/3dbe51877a24daa467f8d8631f9ffd1aabbf0f6d9367a01c44a59df81fe0/mypy-1.14.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1daca283d732943731a6a9f20fdbcaa927f160bc51602b1d4ef880a6fb252015", size = 12746528 },
{ url = "https://files.pythonhosted.org/packages/3f/1d/676e76f07f7d5ddcd4227af3938a9c9640f293b7d8a44dd4ff41d4db25c1/mypy-1.13.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3790ded76f0b34bc9c8ba4def8f919dd6a46db0f5a6610fb994fe8efdd447f73", size = 12984688 }, { url = "https://files.pythonhosted.org/packages/a1/a8/eb20cde4ba9c4c3e20d958918a7c5d92210f4d1a0200c27de9a641f70996/mypy-1.14.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7e68047bedb04c1c25bba9901ea46ff60d5eaac2d71b1f2161f33107e2b368eb", size = 12883489 },
{ url = "https://files.pythonhosted.org/packages/9c/03/5a85a30ae5407b1d28fab51bd3e2103e52ad0918d1e68f02a7778669a307/mypy-1.13.0-cp311-cp311-win_amd64.whl", hash = "sha256:51f869f4b6b538229c1d1bcc1dd7d119817206e2bc54e8e374b3dfa202defcca", size = 9626811 }, { url = "https://files.pythonhosted.org/packages/91/17/a1fc6c70f31d52c99299320cf81c3cb2c6b91ec7269414e0718a6d138e34/mypy-1.14.0-cp311-cp311-win_amd64.whl", hash = "sha256:7a52f26b9c9b1664a60d87675f3bae00b5c7f2806e0c2800545a32c325920bcc", size = 9780113 },
{ url = "https://files.pythonhosted.org/packages/fb/31/c526a7bd2e5c710ae47717c7a5f53f616db6d9097caf48ad650581e81748/mypy-1.13.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:5c7051a3461ae84dfb5dd15eff5094640c61c5f22257c8b766794e6dd85e72d5", size = 11077900 }, { url = "https://files.pythonhosted.org/packages/fe/d8/0e72175ee0253217f5c44524f5e95251c02e95ba9749fb87b0e2074d203a/mypy-1.14.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d5326ab70a6db8e856d59ad4cb72741124950cbbf32e7b70e30166ba7bbf61dd", size = 11269011 },
{ url = "https://files.pythonhosted.org/packages/83/67/b7419c6b503679d10bd26fc67529bc6a1f7a5f220bbb9f292dc10d33352f/mypy-1.13.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:39bb21c69a5d6342f4ce526e4584bc5c197fd20a60d14a8624d8743fffb9472e", size = 10074818 }, { url = "https://files.pythonhosted.org/packages/e9/6d/4ea13839dabe5db588dc6a1b766da16f420d33cf118a7b7172cdf6c7fcb2/mypy-1.14.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:bf4ec4980bec1e0e24e5075f449d014011527ae0055884c7e3abc6a99cd2c7f1", size = 10253076 },
{ url = "https://files.pythonhosted.org/packages/ba/07/37d67048786ae84e6612575e173d713c9a05d0ae495dde1e68d972207d98/mypy-1.13.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:164f28cb9d6367439031f4c81e84d3ccaa1e19232d9d05d37cb0bd880d3f93c2", size = 12589275 }, { url = "https://files.pythonhosted.org/packages/3e/38/7db2c5d0f4d290e998f7a52b2e2616c7bbad96b8e04278ab09d11978a29e/mypy-1.14.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:390dfb898239c25289495500f12fa73aa7f24a4c6d90ccdc165762462b998d63", size = 12862786 },
{ url = "https://files.pythonhosted.org/packages/1f/17/b1018c6bb3e9f1ce3956722b3bf91bff86c1cefccca71cec05eae49d6d41/mypy-1.13.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a4c1bfcdbce96ff5d96fc9b08e3831acb30dc44ab02671eca5953eadad07d6d0", size = 13037783 }, { url = "https://files.pythonhosted.org/packages/bf/4b/62d59c801b34141040989949c2b5c157d0408b45357335d3ec5b2845b0f6/mypy-1.14.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7e026d55ddcd76e29e87865c08cbe2d0104e2b3153a523c529de584759379d3d", size = 12971568 },
{ url = "https://files.pythonhosted.org/packages/cb/32/cd540755579e54a88099aee0287086d996f5a24281a673f78a0e14dba150/mypy-1.13.0-cp312-cp312-win_amd64.whl", hash = "sha256:a0affb3a79a256b4183ba09811e3577c5163ed06685e4d4b46429a271ba174d2", size = 9726197 }, { url = "https://files.pythonhosted.org/packages/f1/9c/e0f281b32d70c87b9e4d2939e302b1ff77ada4d7b0f2fb32890c144bc1d6/mypy-1.14.0-cp312-cp312-win_amd64.whl", hash = "sha256:585ed36031d0b3ee362e5107ef449a8b5dfd4e9c90ccbe36414ee405ee6b32ba", size = 9879477 },
{ url = "https://files.pythonhosted.org/packages/11/bb/ab4cfdc562cad80418f077d8be9b4491ee4fb257440da951b85cbb0a639e/mypy-1.13.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a7b44178c9760ce1a43f544e595d35ed61ac2c3de306599fa59b38a6048e1aa7", size = 11069721 }, { url = "https://files.pythonhosted.org/packages/13/33/8380efd0ebdfdfac7fc0bf065f03a049800ca1e6c296ec1afc634340d992/mypy-1.14.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e9f6f4c0b27401d14c483c622bc5105eff3911634d576bbdf6695b9a7c1ba741", size = 11251509 },
{ url = "https://files.pythonhosted.org/packages/59/3b/a393b1607cb749ea2c621def5ba8c58308ff05e30d9dbdc7c15028bca111/mypy-1.13.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5d5092efb8516d08440e36626f0153b5006d4088c1d663d88bf79625af3d1d62", size = 10063996 }, { url = "https://files.pythonhosted.org/packages/15/6d/4e1c21c60fee11af7d8e4f2902a29886d1387d6a836be16229eb3982a963/mypy-1.14.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:56b2280cedcb312c7a79f5001ae5325582d0d339bce684e4a529069d0e7ca1e7", size = 10244282 },
{ url = "https://files.pythonhosted.org/packages/d1/1f/6b76be289a5a521bb1caedc1f08e76ff17ab59061007f201a8a18cc514d1/mypy-1.13.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:de2904956dac40ced10931ac967ae63c5089bd498542194b436eb097a9f77bc8", size = 12584043 }, { url = "https://files.pythonhosted.org/packages/8b/cf/7a8ae5c0161edae15d25c2c67c68ce8b150cbdc45aefc13a8be271ee80b2/mypy-1.14.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:342de51c48bab326bfc77ce056ba08c076d82ce4f5a86621f972ed39970f94d8", size = 12867676 },
{ url = "https://files.pythonhosted.org/packages/a6/83/5a85c9a5976c6f96e3a5a7591aa28b4a6ca3a07e9e5ba0cec090c8b596d6/mypy-1.13.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:7bfd8836970d33c2105562650656b6846149374dc8ed77d98424b40b09340ba7", size = 13036996 }, { url = "https://files.pythonhosted.org/packages/9c/d0/71f7bbdcc7cfd0f2892db5b13b1e8857673f2cc9e0c30e3e4340523dc186/mypy-1.14.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:00df23b42e533e02a6f0055e54de9a6ed491cd8b7ea738647364fd3a39ea7efc", size = 12964189 },
{ url = "https://files.pythonhosted.org/packages/b4/59/c39a6f752f1f893fccbcf1bdd2aca67c79c842402b5283563d006a67cf76/mypy-1.13.0-cp313-cp313-win_amd64.whl", hash = "sha256:9f73dba9ec77acb86457a8fc04b5239822df0c14a082564737833d2963677dbc", size = 9737709 }, { url = "https://files.pythonhosted.org/packages/a7/40/fb4ad65d6d5f8c51396ecf6305ec0269b66013a5bf02d0e9528053640b4a/mypy-1.14.0-cp313-cp313-win_amd64.whl", hash = "sha256:e8c8387e5d9dff80e7daf961df357c80e694e942d9755f3ad77d69b0957b8e3f", size = 9888247 },
{ url = "https://files.pythonhosted.org/packages/3b/86/72ce7f57431d87a7ff17d442f521146a6585019eb8f4f31b7c02801f78ad/mypy-1.13.0-py3-none-any.whl", hash = "sha256:9c250883f9fd81d212e0952c92dbfcc96fc237f4b7c92f56ac81fd48460b3e5a", size = 2647043 }, { url = "https://files.pythonhosted.org/packages/39/32/0214608af400cdf8f5102144bb8af10d880675c65ed0b58f7e0e77175d50/mypy-1.14.0-py3-none-any.whl", hash = "sha256:2238d7f93fc4027ed1efc944507683df3ba406445a2b6c96e79666a045aadfab", size = 2752803 },
] ]
[[package]] [[package]]
@ -6098,7 +6098,7 @@ name = "portalocker"
version = "2.10.1" version = "2.10.1"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "pywin32", marker = "platform_system == 'Windows'" }, { name = "pywin32", marker = "sys_platform == 'win32'" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/ed/d3/c6c64067759e87af98cc668c1cc75171347d0f1577fab7ca3749134e3cd4/portalocker-2.10.1.tar.gz", hash = "sha256:ef1bf844e878ab08aee7e40184156e1151f228f103aa5c6bd0724cc330960f8f", size = 40891 } sdist = { url = "https://files.pythonhosted.org/packages/ed/d3/c6c64067759e87af98cc668c1cc75171347d0f1577fab7ca3749134e3cd4/portalocker-2.10.1.tar.gz", hash = "sha256:ef1bf844e878ab08aee7e40184156e1151f228f103aa5c6bd0724cc330960f8f", size = 40891 }
wheels = [ wheels = [
@ -7731,27 +7731,27 @@ wheels = [
[[package]] [[package]]
name = "ruff" name = "ruff"
version = "0.8.2" version = "0.8.4"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/5e/2b/01245f4f3a727d60bebeacd7ee6d22586c7f62380a2597ddb22c2f45d018/ruff-0.8.2.tar.gz", hash = "sha256:b84f4f414dda8ac7f75075c1fa0b905ac0ff25361f42e6d5da681a465e0f78e5", size = 3349020 } sdist = { url = "https://files.pythonhosted.org/packages/34/37/9c02181ef38d55b77d97c68b78e705fd14c0de0e5d085202bb2b52ce5be9/ruff-0.8.4.tar.gz", hash = "sha256:0d5f89f254836799af1615798caa5f80b7f935d7a670fad66c5007928e57ace8", size = 3402103 }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/91/29/366be70216dba1731a00a41f2f030822b0c96c7c4f3b2c0cdce15cbace74/ruff-0.8.2-py3-none-linux_armv6l.whl", hash = "sha256:c49ab4da37e7c457105aadfd2725e24305ff9bc908487a9bf8d548c6dad8bb3d", size = 10530649 }, { url = "https://files.pythonhosted.org/packages/05/67/f480bf2f2723b2e49af38ed2be75ccdb2798fca7d56279b585c8f553aaab/ruff-0.8.4-py3-none-linux_armv6l.whl", hash = "sha256:58072f0c06080276804c6a4e21a9045a706584a958e644353603d36ca1eb8a60", size = 10546415 },
{ url = "https://files.pythonhosted.org/packages/63/82/a733956540bb388f00df5a3e6a02467b16c0e529132625fe44ce4c5fb9c7/ruff-0.8.2-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:ec016beb69ac16be416c435828be702ee694c0d722505f9c1f35e1b9c0cc1bf5", size = 10274069 }, { url = "https://files.pythonhosted.org/packages/eb/7a/5aba20312c73f1ce61814e520d1920edf68ca3b9c507bd84d8546a8ecaa8/ruff-0.8.4-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:ffb60904651c00a1e0b8df594591770018a0f04587f7deeb3838344fe3adabac", size = 10346113 },
{ url = "https://files.pythonhosted.org/packages/3d/12/0b3aa14d1d71546c988a28e1b412981c1b80c8a1072e977a2f30c595cc4a/ruff-0.8.2-py3-none-macosx_11_0_arm64.whl", hash = "sha256:f05cdf8d050b30e2ba55c9b09330b51f9f97d36d4673213679b965d25a785f3c", size = 9909400 }, { url = "https://files.pythonhosted.org/packages/76/f4/c41de22b3728486f0aa95383a44c42657b2db4062f3234ca36fc8cf52d8b/ruff-0.8.4-py3-none-macosx_11_0_arm64.whl", hash = "sha256:6ddf5d654ac0d44389f6bf05cee4caeefc3132a64b58ea46738111d687352296", size = 9943564 },
{ url = "https://files.pythonhosted.org/packages/23/08/f9f08cefb7921784c891c4151cce6ed357ff49e84b84978440cffbc87408/ruff-0.8.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:60f578c11feb1d3d257b2fb043ddb47501ab4816e7e221fbb0077f0d5d4e7b6f", size = 10766782 }, { url = "https://files.pythonhosted.org/packages/0e/f0/afa0d2191af495ac82d4cbbfd7a94e3df6f62a04ca412033e073b871fc6d/ruff-0.8.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e248b1f0fa2749edd3350a2a342b67b43a2627434c059a063418e3d375cfe643", size = 10805522 },
{ url = "https://files.pythonhosted.org/packages/e4/71/bf50c321ec179aa420c8ec40adac5ae9cc408d4d37283a485b19a2331ceb/ruff-0.8.2-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:cbd5cf9b0ae8f30eebc7b360171bd50f59ab29d39f06a670b3e4501a36ba5897", size = 10286316 }, { url = "https://files.pythonhosted.org/packages/12/57/5d1e9a0fd0c228e663894e8e3a8e7063e5ee90f8e8e60cf2085f362bfa1a/ruff-0.8.4-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bf197b98ed86e417412ee3b6c893f44c8864f816451441483253d5ff22c0e81e", size = 10306763 },
{ url = "https://files.pythonhosted.org/packages/f2/83/c82688a2a6117539aea0ce63fdf6c08e60fe0202779361223bcd7f40bd74/ruff-0.8.2-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b402ddee3d777683de60ff76da801fa7e5e8a71038f57ee53e903afbcefdaa58", size = 11338270 }, { url = "https://files.pythonhosted.org/packages/04/df/f069fdb02e408be8aac6853583572a2873f87f866fe8515de65873caf6b8/ruff-0.8.4-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c41319b85faa3aadd4d30cb1cffdd9ac6b89704ff79f7664b853785b48eccdf3", size = 11359574 },
{ url = "https://files.pythonhosted.org/packages/7f/d7/bc6a45e5a22e627640388e703160afb1d77c572b1d0fda8b4349f334fc66/ruff-0.8.2-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:705832cd7d85605cb7858d8a13d75993c8f3ef1397b0831289109e953d833d29", size = 12058579 }, { url = "https://files.pythonhosted.org/packages/d3/04/37c27494cd02e4a8315680debfc6dfabcb97e597c07cce0044db1f9dfbe2/ruff-0.8.4-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:9f8402b7c4f96463f135e936d9ab77b65711fcd5d72e5d67597b543bbb43cf3f", size = 12094851 },
{ url = "https://files.pythonhosted.org/packages/da/3b/64150c93946ec851e6f1707ff586bb460ca671581380c919698d6a9267dc/ruff-0.8.2-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:32096b41aaf7a5cc095fa45b4167b890e4c8d3fd217603f3634c92a541de7248", size = 11615172 }, { url = "https://files.pythonhosted.org/packages/81/b1/c5d7fb68506cab9832d208d03ea4668da9a9887a4a392f4f328b1bf734ad/ruff-0.8.4-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e4e56b3baa9c23d324ead112a4fdf20db9a3f8f29eeabff1355114dd96014604", size = 11655539 },
{ url = "https://files.pythonhosted.org/packages/e4/9e/cf12b697ea83cfe92ec4509ae414dc4c9b38179cc681a497031f0d0d9a8e/ruff-0.8.2-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e769083da9439508833cfc7c23e351e1809e67f47c50248250ce1ac52c21fb93", size = 12882398 }, { url = "https://files.pythonhosted.org/packages/ef/38/8f8f2c8898dc8a7a49bc340cf6f00226917f0f5cb489e37075bcb2ce3671/ruff-0.8.4-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:736272574e97157f7edbbb43b1d046125fce9e7d8d583d5d65d0c9bf2c15addf", size = 12912805 },
{ url = "https://files.pythonhosted.org/packages/a9/27/96d10863accf76a9c97baceac30b0a52d917eb985a8ac058bd4636aeede0/ruff-0.8.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5fe716592ae8a376c2673fdfc1f5c0c193a6d0411f90a496863c99cd9e2ae25d", size = 11176094 }, { url = "https://files.pythonhosted.org/packages/06/dd/fa6660c279f4eb320788876d0cff4ea18d9af7d9ed7216d7bd66877468d0/ruff-0.8.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e5fe710ab6061592521f902fca7ebcb9fabd27bc7c57c764298b1c1f15fff720", size = 11205976 },
{ url = "https://files.pythonhosted.org/packages/eb/10/cd2fd77d4a4e7f03c29351be0f53278a393186b540b99df68beb5304fddd/ruff-0.8.2-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:81c148825277e737493242b44c5388a300584d73d5774defa9245aaef55448b0", size = 10771884 }, { url = "https://files.pythonhosted.org/packages/a8/d7/de94cc89833b5de455750686c17c9e10f4e1ab7ccdc5521b8fe911d1477e/ruff-0.8.4-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:13e9ec6d6b55f6da412d59953d65d66e760d583dd3c1c72bf1f26435b5bfdbae", size = 10792039 },
{ url = "https://files.pythonhosted.org/packages/71/5d/beabb2ff18870fc4add05fa3a69a4cb1b1d2d6f83f3cf3ae5ab0d52f455d/ruff-0.8.2-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:d261d7850c8367704874847d95febc698a950bf061c9475d4a8b7689adc4f7fa", size = 10382535 }, { url = "https://files.pythonhosted.org/packages/6d/15/3e4906559248bdbb74854af684314608297a05b996062c9d72e0ef7c7097/ruff-0.8.4-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:97d9aefef725348ad77d6db98b726cfdb075a40b936c7984088804dfd38268a7", size = 10400088 },
{ url = "https://files.pythonhosted.org/packages/ae/29/6b3fdf3ad3e35b28d87c25a9ff4c8222ad72485ab783936b2b267250d7a7/ruff-0.8.2-py3-none-musllinux_1_2_i686.whl", hash = "sha256:1ca4e3a87496dc07d2427b7dd7ffa88a1e597c28dad65ae6433ecb9f2e4f022f", size = 10886995 }, { url = "https://files.pythonhosted.org/packages/a2/21/9ed4c0e8133cb4a87a18d470f534ad1a8a66d7bec493bcb8bda2d1a5d5be/ruff-0.8.4-py3-none-musllinux_1_2_i686.whl", hash = "sha256:ab78e33325a6f5374e04c2ab924a3367d69a0da36f8c9cb6b894a62017506111", size = 10900814 },
{ url = "https://files.pythonhosted.org/packages/e9/dc/859d889b4d9356a1a2cdbc1e4a0dda94052bc5b5300098647e51a58c430b/ruff-0.8.2-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:729850feed82ef2440aa27946ab39c18cb4a8889c1128a6d589ffa028ddcfc22", size = 11220750 }, { url = "https://files.pythonhosted.org/packages/0d/5d/122a65a18955bd9da2616b69bc839351f8baf23b2805b543aa2f0aed72b5/ruff-0.8.4-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:8ef06f66f4a05c3ddbc9121a8b0cecccd92c5bf3dd43b5472ffe40b8ca10f0f8", size = 11268828 },
{ url = "https://files.pythonhosted.org/packages/0b/08/e8f519f61f1d624264bfd6b8829e4c5f31c3c61193bc3cff1f19dbe7626a/ruff-0.8.2-py3-none-win32.whl", hash = "sha256:ac42caaa0411d6a7d9594363294416e0e48fc1279e1b0e948391695db2b3d5b1", size = 8729396 }, { url = "https://files.pythonhosted.org/packages/43/a9/1676ee9106995381e3d34bccac5bb28df70194167337ed4854c20f27c7ba/ruff-0.8.4-py3-none-win32.whl", hash = "sha256:552fb6d861320958ca5e15f28b20a3d071aa83b93caee33a87b471f99a6c0835", size = 8805621 },
{ url = "https://files.pythonhosted.org/packages/f8/d4/ba1c7ab72aba37a2b71fe48ab95b80546dbad7a7f35ea28cf66fc5cea5f6/ruff-0.8.2-py3-none-win_amd64.whl", hash = "sha256:2aae99ec70abf43372612a838d97bfe77d45146254568d94926e8ed5bbb409ea", size = 9594729 }, { url = "https://files.pythonhosted.org/packages/10/98/ed6b56a30ee76771c193ff7ceeaf1d2acc98d33a1a27b8479cbdb5c17a23/ruff-0.8.4-py3-none-win_amd64.whl", hash = "sha256:f21a1143776f8656d7f364bd264a9d60f01b7f52243fbe90e7670c0dfe0cf65d", size = 9660086 },
{ url = "https://files.pythonhosted.org/packages/23/34/db20e12d3db11b8a2a8874258f0f6d96a9a4d631659d54575840557164c8/ruff-0.8.2-py3-none-win_arm64.whl", hash = "sha256:fb88e2a506b70cfbc2de6fae6681c4f944f7dd5f2fe87233a7233d888bad73e8", size = 9035131 }, { url = "https://files.pythonhosted.org/packages/13/9f/026e18ca7d7766783d779dae5e9c656746c6ede36ef73c6d934aaf4a6dec/ruff-0.8.4-py3-none-win_arm64.whl", hash = "sha256:9183dd615d8df50defa8b1d9a074053891ba39025cf5ae88e8bcb52edcc4bf08", size = 9074500 },
] ]
[[package]] [[package]]
@ -8575,19 +8575,19 @@ dependencies = [
{ name = "fsspec" }, { name = "fsspec" },
{ name = "jinja2" }, { name = "jinja2" },
{ name = "networkx" }, { name = "networkx" },
{ name = "nvidia-cublas-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, { name = "nvidia-cublas-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" },
{ name = "nvidia-cuda-cupti-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, { name = "nvidia-cuda-cupti-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" },
{ name = "nvidia-cuda-nvrtc-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, { name = "nvidia-cuda-nvrtc-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" },
{ name = "nvidia-cuda-runtime-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, { name = "nvidia-cuda-runtime-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" },
{ name = "nvidia-cudnn-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, { name = "nvidia-cudnn-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" },
{ name = "nvidia-cufft-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, { name = "nvidia-cufft-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" },
{ name = "nvidia-curand-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, { name = "nvidia-curand-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" },
{ name = "nvidia-cusolver-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, { name = "nvidia-cusolver-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" },
{ name = "nvidia-cusparse-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, { name = "nvidia-cusparse-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" },
{ name = "nvidia-nccl-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, { name = "nvidia-nccl-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" },
{ name = "nvidia-nvtx-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, { name = "nvidia-nvtx-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" },
{ name = "sympy" }, { name = "sympy" },
{ name = "triton", marker = "python_full_version < '3.13' and platform_machine == 'x86_64' and platform_system == 'Linux'" }, { name = "triton", marker = "python_full_version < '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux'" },
{ name = "typing-extensions" }, { name = "typing-extensions" },
] ]
wheels = [ wheels = [
@ -8628,7 +8628,7 @@ name = "tqdm"
version = "4.67.1" version = "4.67.1"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "colorama", marker = "platform_system == 'Windows'" }, { name = "colorama", marker = "sys_platform == 'win32'" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/a8/4b/29b4ef32e036bb34e4ab51796dd745cdba7ed47ad142a9f4a1eb8e0c744d/tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2", size = 169737 } sdist = { url = "https://files.pythonhosted.org/packages/a8/4b/29b4ef32e036bb34e4ab51796dd745cdba7ed47ad142a9f4a1eb8e0c744d/tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2", size = 169737 }
wheels = [ wheels = [