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",
"ipykernel>=6.29.0",
"mypy>=1.11.0",
"ruff>=0.8.2,<0.9.0",
"ruff>=0.8.4,<0.9.0",
"httpx>=0.27.0",
"pytest>=8.2.0",
"types-requests>=2.32.0",

View file

@ -90,7 +90,7 @@ class AgentQL(Component):
except httpx.HTTPStatusError as e:
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."
else:
try:

View file

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

View file

@ -131,12 +131,12 @@ class LangWatchComponent(Component):
# Clear component's dynamic attributes
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",
"dynamic_inputs",
"_code",
"current_evaluator",
]:
}:
delattr(self, attr)
# Add new dynamic inputs
@ -177,7 +177,7 @@ class LangWatchComponent(Component):
input_fields = [
field
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:

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:
if field_name == "operator":
if field_value == "matches regex":
if "case_sensitive" in build_config:
del build_config["case_sensitive"]
build_config.pop("case_sensitive", None)
# Ensure case_sensitive is present for all other operators
elif "case_sensitive" not in build_config:
case_sensitive_input = next(

View file

@ -22,7 +22,7 @@ class OpenRouterComponent(LCModelComponent):
display_name = "OpenRouter"
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"
@ -180,9 +180,7 @@ class OpenRouterComponent(LCModelComponent):
build_config["model_name"]["value"] = models[0]["id"]
tooltips = {
model["id"]: (
f"{model['name']}\n" f"Context Length: {model['context_length']}\n" f"{model['description']}"
)
model["id"]: (f"{model['name']}\nContext Length: {model['context_length']}\n{model['description']}")
for model in models
}
build_config["model_name"]["tooltips"] = tooltips

View file

@ -10,7 +10,7 @@ from langflow.utils.constants import MESSAGE_SENDER_AI
class NeedleComponent(Component):
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"
icon = "search"
name = "needle"
@ -105,8 +105,8 @@ class NeedleComponent(Component):
if str(output_type).lower().strip() == "chunks":
# If chunks selected, include full context and answer
docs = result["source_documents"]
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']}"
context = "\n\n".join([f"Document {i + 1}:\n{doc.page_content}" for i, doc in enumerate(docs)])
text_content = f"Question: {query}\n\nContext:\n{context}\n\nAnswer: {result['answer']}"
else:
# If answer selected, only include the answer
text_content = result["answer"]

View file

@ -144,7 +144,7 @@ class DataFrameOperationsComponent(Component):
build_config["new_column_value"]["show"] = True
elif field_value == "Select Columns":
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
elif field_value == "Replace Value":
build_config["column_name"]["show"] = True

View file

@ -74,7 +74,7 @@ class MergeDataComponent(Component):
for key, value in data_input.data.items():
if key in result_data and isinstance(value, str):
if isinstance(result_data[key], list):
cast(list[str], result_data[key]).append(value)
cast("list[str]", result_data[key]).append(value)
else:
result_data[key] = [result_data[key], value]
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):
# 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
build_config["api_endpoint"]["options"] = self._initialize_database_options()

View file

@ -1101,7 +1101,8 @@ class Graph:
return False
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)
if not same_length:
return False
@ -1747,7 +1748,8 @@ class Graph:
new_edge = Edge(source, target, 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."""
# First we check for the node_base_type
node_name = node_id.split("-")[0]
@ -1830,7 +1832,8 @@ class Graph:
self._record_snapshot()
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."""
children = []
vertex_types = [vertex.data["type"]]
@ -2059,7 +2062,8 @@ class Graph:
self._first_layer = 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."""
def contains_interface_component(vertex):
@ -2097,9 +2101,6 @@ class 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.
Returns:
None
"""
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
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."""
predecessor_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:
template (dict): The new template to update the node with.
g_nodes (list): The list of nodes in the graph.
Returns:
None
"""
for value in template.values():
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.
target_handle (dict): The target handle of the edge.
node (dict): The node containing the edge.
Returns:
None
"""
new_edge["target"] = proxy_id
type_ = target_handle.get("type")

View file

@ -423,7 +423,7 @@ class Vertex:
else:
msg = f"Invalid value type {type(val)} for field {field_name}"
raise ValueError(msg)
elif val is not None and val != "":
elif val:
params[field_name] = val
if field.get("load_from_db"):
@ -596,7 +596,8 @@ class Vertex:
result = await value.get_result(self, target_handle_name=key)
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."""
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.utils.lazy_load import LazyLoadDictBase
@ -13,6 +15,7 @@ class AllTypesDict(LazyLoadDictBase):
"Custom": ["Custom Tool", "Python Function"],
}
@override
def get_type_dict(self):
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 platformdirs import user_cache_dir
from rich.logging import RichHandler
from typing_extensions import NotRequired
from typing_extensions import NotRequired, override
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.
"""
@override
def emit(self, record) -> None:
# Get corresponding Loguru level if it exists
try:

View file

@ -19,7 +19,7 @@ from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor
from pydantic import PydanticDeprecatedSince20
from pydantic_core import PydanticSerializationError
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.initial_setup.setup import (
@ -45,7 +45,7 @@ class RequestCancelledMiddleware(BaseHTTPMiddleware):
def __init__(self, app) -> None:
super().__init__(app)
async def dispatch(self, request: Request, call_next):
async def dispatch(self, request: Request, call_next: RequestResponseEndpoint) -> Response:
sentinel = object()
async def cancel_handler():
@ -68,7 +68,7 @@ class RequestCancelledMiddleware(BaseHTTPMiddleware):
class JavaScriptMIMETypeMiddleware(BaseHTTPMiddleware):
async def dispatch(self, request: Request, call_next):
async def dispatch(self, request: Request, call_next: RequestResponseEndpoint) -> Response:
try:
response = await call_next(request)
except Exception as exc:

View file

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

View file

@ -32,7 +32,7 @@ class DataFrame(pandas_DataFrame):
>>> 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:
super().__init__(**kwargs)
return

View file

@ -6,7 +6,7 @@ from typing_extensions import Protocol
from langflow.schema.message import ContentBlock, Message
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):

View file

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

View file

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

View file

@ -187,7 +187,8 @@ class DatabaseService(Service):
await session.commit()
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."""
if original_name not in existing_names:
return original_name
@ -212,7 +213,8 @@ class DatabaseService(Service):
return new_name
def _check_schema_health(self, connection) -> bool:
@staticmethod
def _check_schema_health(connection) -> bool:
inspector = inspect(connection)
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:
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")
command.ensure_version(alembic_cfg)
# alembic_cfg.attributes["connection"].commit()
@ -317,7 +320,8 @@ class DatabaseService(Service):
should_initialize_alembic = True
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.
# until we reach the number of retries
for i in range(1, retries + 1):

View file

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

View file

@ -38,7 +38,8 @@ class SessionService(Service):
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)
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.settings.service import SettingsService
@ -13,6 +15,7 @@ class SettingsServiceFactory(ServiceFactory):
def __init__(self) -> None:
super().__init__(SettingsService)
@override
def create(self):
# Here you would have logic to create and configure a SettingsService

View file

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

View file

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

View file

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

View file

@ -1,4 +1,5 @@
from loguru import logger
from typing_extensions import override
from langflow.services.factory import ServiceFactory
from langflow.services.session.service import SessionService
@ -12,6 +13,7 @@ class StorageServiceFactory(ServiceFactory):
StorageService,
)
@override
def create(self, session_service: SessionService, settings_service: SettingsService):
storage_type = settings_service.settings.storage_type
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:
"""Save a file in the local storage.
:param flow_id: The identifier for the flow.
:param file_name: The name of the file to be saved.
:param data: The byte content of the file.
:raises FileNotFoundError: If the specified flow does not exist.
:raises IsADirectoryError: If the file name is a directory.
:raises PermissionError: If there is no permission to write the file.
Args:
flow_id: The identifier for the flow.
file_name: The name of the file to be saved.
data: The byte content of 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
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:
"""Retrieve a file from the local storage.
:param flow_id: The identifier for the flow.
:param file_name: The name of the file to be retrieved.
:return: The byte content of the file.
:raises FileNotFoundError: If the file does not exist.
Args:
flow_id: The identifier for the flow.
file_name: The name of the file to be retrieved.
Returns:
The byte content of the file.
Raises:
FileNotFoundError: If the file does not exist.
"""
file_path = self.data_dir / flow_id / file_name
if not await file_path.exists():
@ -63,9 +71,14 @@ class LocalStorageService(StorageService):
async def list_files(self, flow_id: str):
"""List all files in a specified flow.
:param flow_id: The identifier for the flow.
:return: A list of file names.
:raises FileNotFoundError: If the flow directory does not exist.
Args:
flow_id: The identifier for the flow.
Returns:
A list of file names.
Raises:
FileNotFoundError: If the flow directory does not exist.
"""
if not isinstance(flow_id, str):
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:
"""Save a file to the S3 bucket.
:param folder: The folder in the bucket to save the file.
:param file_name: The name of the file to be saved.
:param data: The byte content of the file.
:raises Exception: If an error occurs during file saving.
Args:
folder: The folder in the bucket to save the file.
file_name: The name of the file to be saved.
data: The byte content of the file.
Raises:
Exception: If an error occurs during file saving.
"""
try:
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):
"""Retrieve a file from the S3 bucket.
:param folder: The folder in the bucket where the file is stored.
:param file_name: The name of the file to be retrieved.
:return: The byte content of the file.
:raises Exception: If an error occurs during file retrieval.
Args:
folder: The folder in the bucket where the file is stored.
file_name: The name of the file to be retrieved.
Returns:
The byte content of the file.
Raises:
Exception: If an error occurs during file retrieval.
"""
try:
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):
"""List all files in a specified folder of the S3 bucket.
:param folder: The folder in the bucket to list files from.
:return: A list of file names.
:raises Exception: If an error occurs during file listing.
Args:
folder: The folder in the bucket to list files from.
Returns:
A list of file names.
Raises:
Exception: If an error occurs during file listing.
"""
try:
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:
"""Delete a file from the S3 bucket.
:param folder: The folder in the bucket where the file is stored.
:param file_name: The name of the file to be deleted.
:raises Exception: If an error occurs during file deletion.
Args:
folder: The folder in the bucket where the file is stored.
file_name: The name of the file to be deleted.
Raises:
Exception: If an error occurs during file deletion.
"""
try:
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_extensions import override
from langflow.services.factory import ServiceFactory
from langflow.services.store.service import StoreService
@ -13,5 +15,6 @@ class StoreServiceFactory(ServiceFactory):
def __init__(self) -> None:
super().__init__(StoreService)
@override
def create(self, settings_service: SettingsService):
return StoreService(settings_service)

View file

@ -164,7 +164,8 @@ class StoreService(Service):
except Exception: # noqa: BLE001
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": []}}
for tag in tags:
tags_filter["tags"]["_and"].append({"_some": {"tags_id": {"name": {"_eq": tag}}}})
@ -249,7 +250,8 @@ class StoreService(Service):
return filter_conditions
def build_liked_filter(self):
@staticmethod
def build_liked_filter():
user_data = user_data_var.get()
# params["filter"] = json.dumps({"user_created": {"_eq": user_data["id"]}})
if not user_data:

View file

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

View file

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

View file

@ -132,7 +132,8 @@ class TelemetryService(Service):
except Exception: # noqa: BLE001
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)
await asyncio.wait([task])
if not task.cancelled():

View file

@ -319,7 +319,8 @@ class ArizePhoenixTracer(BaseTracer):
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."""
error_message = None
if error:
@ -327,11 +328,13 @@ class ArizePhoenixTracer(BaseTracer):
error_message = f"{error.__class__.__name__}: {error}\n\n{string_stacktrace}"
return error_message
def _get_current_timestamp(self) -> int:
@staticmethod
def _get_current_timestamp() -> int:
"""Gets the current UTC timestamp in nanoseconds."""
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."""
return json.dumps(obj, default=str, ensure_ascii=False, **kwargs)
@ -359,6 +362,7 @@ class ArizePhoenixTracer(BaseTracer):
else:
current_span.set_status(Status(StatusCode.OK))
@override
def get_langchain_callback(self) -> BaseCallbackHandler | None:
"""Returns the LangChain callback handler if applicable."""
return None

View file

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

View file

@ -135,7 +135,8 @@ class LangFuseTracer(BaseTracer):
return None
return None # self._callback
def _get_config(self) -> dict:
@staticmethod
def _get_config() -> dict:
secret_key = os.getenv("LANGFUSE_SECRET_KEY", None)
public_key = os.getenv("LANGFUSE_PUBLIC_KEY", 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 loguru import logger
from typing_extensions import override
from langflow.schema.data import Data
from langflow.services.tracing.base import BaseTracer
@ -140,7 +141,8 @@ class LangSmithTracer(BaseTracer):
child.post()
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
if error:
string_stacktrace = traceback.format_exception(error)
@ -163,5 +165,6 @@ class LangSmithTracer(BaseTracer):
self._run_tree.post()
self._run_link = self._run_tree.get_url()
@override
def get_langchain_callback(self) -> BaseCallbackHandler | None:
return None

View file

@ -264,7 +264,8 @@ class TracingService(Service):
self.outputs[trace_name] |= outputs 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()
for key in inputs:
if "api_key" in key:

View file

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

View file

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

View file

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

View file

@ -36,7 +36,8 @@ class KeyedWorkerLockManager:
def __init__(self) -> None:
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.
Parameters:

View file

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

View file

@ -4,7 +4,11 @@ from langflow.utils import constants
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:
max_length = constants.MAX_TEXT_LENGTH

View file

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

View file

@ -1,4 +1,7 @@
from importlib import metadata
import httpx
from packaging import version as pkg_version
from langflow.logging.logger import logger
@ -22,8 +25,6 @@ def _get_version_info():
Raises:
ValueError: If the package is not found from the list of package names.
"""
from importlib import metadata
package_options = [
("langflow", "Langflow"),
("langflow-base", "Langflow Base"),
@ -57,8 +58,9 @@ VERSION_INFO = _get_version_info()
def is_pre_release(v: str) -> bool:
"""Whether the version is a pre-release version.
Returns a boolean indicating whether the version is a pre-release version,
as per the definition of a pre-release segment from PEP 440.
Returns:
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"])
@ -66,15 +68,14 @@ def is_pre_release(v: str) -> bool:
def is_nightly(v: str) -> bool:
"""Whether the version is a dev (nightly) version.
Returns a boolean indicating whether the version is a dev (nightly) version,
as per the definition of a dev segment from PEP 440.
Returns:
Whether the version is a dev (nightly) version,
as per the definition of a dev segment from PEP 440.
"""
return "dev" in v
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()
try:
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)
def build_vertex(self, vertex: Vertex) -> Vertex:
"""Build a vertex."""
"""Build a vertex.
Returns:
The built vertex.
"""
try:
vertex.task_id = self.request.id
async_to_sync(vertex.build)()

View file

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

View file

@ -17,8 +17,10 @@ from blockbuster import blockbuster_ctx
from dotenv import load_dotenv
from fastapi.testclient import TestClient
from httpx import ASGITransport, AsyncClient
from langflow.components.inputs import ChatInput
from langflow.graph import Graph
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.database.models.api_key.model import ApiKey
from langflow.services.database.models.flow.model import Flow, FlowCreate
@ -155,8 +157,6 @@ def caplog(caplog: pytest.LogCaptureFixture):
@pytest.fixture
async def async_client() -> AsyncGenerator:
from langflow.main import create_app
app = create_app()
async with AsyncClient(app=app, base_url="http://testserver", http2=True) as client:
yield client
@ -228,8 +228,6 @@ def distributed_client_fixture(
# def get_session_override():
# return session
from langflow.main import create_app
app = create_app()
# 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_AUTO_LOGIN", "true")
from langflow.main import create_app
app = create_app()
db_service = get_db_service()
db_service.database_url = f"sqlite:///{db_path}"
@ -482,8 +478,6 @@ async def flow(
json_flow: str,
active_user,
):
from langflow.services.database.models.flow.model import FlowCreate
loaded_json = json.loads(json_flow)
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
async def flow_component(client: AsyncClient, logged_in_headers):
from langflow.components.inputs import ChatInput
chat_input = ChatInput()
graph = Graph(start=chat_input, end=chat_input)
graph_dict = graph.dump(name="Chat Input Component")

View file

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

View file

@ -2,6 +2,7 @@ import os
import pytest
from astrapy.db import AstraDB
from langchain_astradb import AstraDBVectorStore, CollectionVectorServiceOptions
from langchain_core.documents import Document
from langflow.components.embeddings import OpenAIEmbeddingsComponent
from langflow.components.vectorstores import AstraDBVectorStoreComponent
@ -37,8 +38,6 @@ def astradb_client():
@pytest.mark.api_key_required
async def test_base(astradb_client: AstraDB):
from langflow.components.embeddings import OpenAIEmbeddingsComponent
application_token = get_astradb_application_token()
api_endpoint = get_astradb_api_endpoint()
@ -88,8 +87,6 @@ async def test_astra_embeds_and_search():
@pytest.mark.api_key_required
def test_astra_vectorize():
from langchain_astradb import AstraDBVectorStore, CollectionVectorServiceOptions
application_token = get_astradb_application_token()
api_endpoint = get_astradb_api_endpoint()
@ -132,8 +129,6 @@ def test_astra_vectorize():
@pytest.mark.api_key_required
def test_astra_vectorize_with_provider_api_key():
"""Tests vectorize using an openai api key."""
from langchain_astradb import AstraDBVectorStore, CollectionVectorServiceOptions
application_token = get_astradb_application_token()
api_endpoint = get_astradb_api_endpoint()
@ -189,8 +184,6 @@ def test_astra_vectorize_with_provider_api_key():
@pytest.mark.api_key_required
def test_astra_vectorize_passes_authentication():
"""Tests vectorize using the authentication parameter."""
from langchain_astradb import AstraDBVectorStore, CollectionVectorServiceOptions
store = None
try:
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
async def test_csv_output_parser_openai():
format_instructions = ComponentInputHandle(
format_instructions_ = ComponentInputHandle(
clazz=OutputParserComponent,
inputs={},
output_name="format_instructions",
@ -24,7 +24,7 @@ async def test_csv_output_parser_openai():
clazz=PromptComponent,
inputs={
"template": "List the first five positive integers.\n\n{format_instructions}",
"format_instructions": format_instructions,
"format_instructions": format_instructions_,
},
output_name="prompt",
)

View file

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

View file

@ -11,6 +11,7 @@ import anyio
import pytest
from asgi_lifespan import LifespanManager
from httpx import ASGITransport, AsyncClient
from langflow.main import create_app
from langflow.services.deps import get_storage_service
from langflow.services.storage.service import StorageService
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_AUTO_LOGIN", "true")
from langflow.main import create_app
app = create_app()
return app, db_path
@ -81,14 +80,14 @@ async def files_client_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")
yield
monkeypatch.undo()
@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")
yield
monkeypatch.undo()

View file

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

View file

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

View file

@ -1,8 +1,11 @@
import re
from unittest.mock import MagicMock, patch
import pytest
from langchain_core.language_models import BaseLanguageModel
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 pydantic import BaseModel
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
# the mock function
def test_successful_structured_output_generation_with_patch_with_config(self):
from unittest.mock import patch
class MockLanguageModel(BaseLanguageModel):
@override
def with_structured_output(self, *args, **kwargs):
@ -87,15 +88,11 @@ class TestStructuredOutputComponent:
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()
# Correctly builds the output model from the provided schema
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
component = StructuredOutputComponent()
schema = [
@ -134,10 +131,6 @@ class TestStructuredOutputComponent:
# Properly handles multiple outputs when 'multiple' is set to True
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
component = StructuredOutputComponent()
schema = [
@ -261,5 +254,5 @@ class TestStructuredOutputComponent:
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()

View file

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

View file

@ -1,3 +1,5 @@
import re
import pytest
from langflow.components.processing import CreateDataComponent
from langflow.schema import Data
@ -48,7 +50,7 @@ def test_update_build_config_exceed_limit(create_data_component):
"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")

View file

@ -1,3 +1,5 @@
import re
import pytest
from langflow.components.processing import UpdateDataComponent
from langflow.schema import Data
@ -48,7 +50,7 @@ def test_update_build_config_exceed_limit(update_data_component):
"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")

View file

@ -14,11 +14,6 @@ from langflow.schema.properties import Properties, Source
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):
time.sleep(0.01)
manager.send_event(event_type=event_type, data=data)
@ -43,7 +38,7 @@ class ComponentForTesting(Component):
async def test_component_message_sending():
"""Test component's message sending functionality."""
# Create event queue and manager
queue = await create_event_queue()
queue = asyncio.Queue()
event_manager = EventManager(queue)
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():
"""Test component's tool output functionality."""
# Create event queue and manager
queue = await create_event_queue()
queue = asyncio.Queue()
event_manager = EventManager(queue)
# Create component
@ -110,7 +105,7 @@ async def test_component_tool_output():
async def test_component_error_handling():
"""Test component's error handling."""
# Create event queue and manager
queue = await create_event_queue()
queue = asyncio.Queue()
event_manager = EventManager(queue)
# Create component
@ -141,7 +136,7 @@ async def test_component_error_handling():
async def test_component_build_results():
"""Test component's build_results functionality."""
# Create event queue and manager
queue = await create_event_queue()
queue = asyncio.Queue()
event_manager = EventManager(queue)
# Create component
@ -173,7 +168,7 @@ async def test_component_build_results():
async def test_component_logging():
"""Test component's logging functionality."""
# Create event queue and manager
queue = await create_event_queue()
queue = asyncio.Queue()
event_manager = EventManager(queue)
# Create component
@ -207,7 +202,7 @@ async def test_component_logging():
@pytest.mark.usefixtures("client")
async def test_component_streaming_message():
"""Test component's streaming message functionality."""
queue = await create_event_queue()
queue = asyncio.Queue()
event_manager = EventManager(queue)
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):
queue = asyncio.Queue()
manager = EventManager(queue)
result = manager.__getattr__("non_registered_event")
result = manager.non_registered_event
assert result == manager.noop
# Accessing a registered event callback via __getattr__
@ -130,8 +130,6 @@ class TestEventManager:
assert len(manager.events) == 1000
# 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):
queue = asyncio.Queue()
manager = EventManager(queue)

View file

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

View file

@ -1,3 +1,5 @@
import re
import pytest
from langflow.components.inputs import ChatInput
from langflow.components.models import OpenAIModelComponent
@ -25,5 +27,7 @@ Answer:
chat_output = ChatOutput()
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)

View file

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

View file

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

View file

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

View file

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

View file

@ -1,3 +1,5 @@
import copy
import pytest
from langchain_core.documents import Document
from langflow.schema import Data
@ -62,8 +64,6 @@ def test_custom_attribute_get_set_del():
def test_deep_copy():
import copy
record1 = Data(data={"text": "Hello", "number": 10})
record2 = copy.deepcopy(record1)
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.utils import session_getter
from langflow.services.deps import get_db_service
from sqlalchemy import text
@pytest.fixture(scope="module")
@ -619,8 +620,6 @@ async def test_sqlite_pragmas():
db_service = get_db_service()
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 synchronous;"))).scalar() == 1

View file

@ -64,7 +64,7 @@ class TestInput:
# Empty lists and edge cases
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[None, list[None]])) == {None, NoneType} # noqa: UP007
assert set(post_process_type(Union[list[None], None])) == {None, NoneType} # noqa: UP007
# Handling complex nested structures
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
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):
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"})

View file

@ -16,51 +16,57 @@ def sample_image(tmp_path):
return image_path
class TestImageUtils:
def test_convert_image_to_base64_success(self, sample_image):
"""Test successful conversion of image to base64."""
base64_str = convert_image_to_base64(sample_image)
assert isinstance(base64_str, str)
# Verify it's valid base64
assert base64.b64decode(base64_str)
def test_convert_image_to_base64_success(sample_image):
"""Test successful conversion of image to base64."""
base64_str = convert_image_to_base64(sample_image)
assert isinstance(base64_str, str)
# Verify it's valid base64
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):
"""Test conversion with non-existent file."""
with pytest.raises(FileNotFoundError, match="Image file not found"):
convert_image_to_base64("nonexistent.png")
def test_convert_image_to_base64_empty_path():
"""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_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):
"""Test successful creation of data URL."""
data_url = create_data_url(sample_image)
assert data_url.startswith("data:image/png;base64,")
# Verify the base64 part is valid
base64_part = data_url.split(",")[1]
assert base64.b64decode(base64_part)
def test_convert_image_to_base64_nonexistent_file():
"""Test conversion with non-existent file."""
with pytest.raises(FileNotFoundError, match="Image file not found"):
convert_image_to_base64("nonexistent.png")
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):
"""Test creation of data URL with invalid file."""
with pytest.raises(FileNotFoundError):
create_data_url("nonexistent.jpg")
def test_convert_image_to_base64_directory(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_unrecognized_extension(self, 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)
def test_create_data_url_success(sample_image):
"""Test successful creation of data URL."""
data_url = create_data_url(sample_image)
assert data_url.startswith("data:image/png;base64,")
# 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 pytest
from langflow.utils.constants import MAX_TEXT_LENGTH
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)
def test_truncate_long_strings_none_max_length():
from langflow.utils.constants import MAX_TEXT_LENGTH
long_string = "a" * (MAX_TEXT_LENGTH + 10)
result = truncate_long_strings(long_string, None)
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"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "jinxed", marker = "platform_system == 'Windows'" },
{ name = "jinxed", marker = "sys_platform == 'win32'" },
{ name = "six" },
{ name = "wcwidth" },
]
@ -962,7 +962,7 @@ name = "click"
version = "8.1.7"
source = { registry = "https://pypi.org/simple" }
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 }
wheels = [
@ -3143,7 +3143,7 @@ name = "ipykernel"
version = "6.29.5"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "appnope", marker = "platform_system == 'Darwin'" },
{ name = "appnope", marker = "sys_platform == 'darwin'" },
{ name = "comm" },
{ name = "debugpy" },
{ name = "ipython" },
@ -3234,7 +3234,7 @@ name = "jinxed"
version = "1.3.0"
source = { registry = "https://pypi.org/simple" }
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 }
wheels = [
@ -4198,7 +4198,7 @@ dev = [
{ name = "pytest-xdist", specifier = ">=3.6.0" },
{ name = "requests", specifier = ">=2.32.0" },
{ 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-google-cloud-ndb", specifier = ">=2.2.0.0" },
{ name = "types-markdown", specifier = ">=3.7.0.20240822" },
@ -5126,36 +5126,36 @@ wheels = [
[[package]]
name = "mypy"
version = "1.13.0"
version = "1.14.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "mypy-extensions" },
{ name = "tomli", marker = "python_full_version < '3.11'" },
{ 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 = [
{ 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/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/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/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/4a/25/3918bc64952370c3dbdbd8c82c363804678127815febd2925b7273d9482c/mypy-1.13.0-cp310-cp310-win_amd64.whl", hash = "sha256:c5fc54dbb712ff5e5a0fca797e6e0aa25726c7e72c6a5850cfd2adbc1eb0a372", size = 9632318 },
{ 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/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/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/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/9c/03/5a85a30ae5407b1d28fab51bd3e2103e52ad0918d1e68f02a7778669a307/mypy-1.13.0-cp311-cp311-win_amd64.whl", hash = "sha256:51f869f4b6b538229c1d1bcc1dd7d119817206e2bc54e8e374b3dfa202defcca", size = 9626811 },
{ 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/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/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/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/cb/32/cd540755579e54a88099aee0287086d996f5a24281a673f78a0e14dba150/mypy-1.13.0-cp312-cp312-win_amd64.whl", hash = "sha256:a0affb3a79a256b4183ba09811e3577c5163ed06685e4d4b46429a271ba174d2", size = 9726197 },
{ 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/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/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/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/b4/59/c39a6f752f1f893fccbcf1bdd2aca67c79c842402b5283563d006a67cf76/mypy-1.13.0-cp313-cp313-win_amd64.whl", hash = "sha256:9f73dba9ec77acb86457a8fc04b5239822df0c14a082564737833d2963677dbc", size = 9737709 },
{ 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/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/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/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/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/b2/69/0c0868a6f3d9761d2f704d1fb6ef84d75998c27d342738a8b20f109a411f/mypy-1.14.0-cp310-cp310-win_amd64.whl", hash = "sha256:3fa76988dc760da377c1e5069200a50d9eaaccf34f4ea18428a3337034ab5a44", size = 9782230 },
{ 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/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/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/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/91/17/a1fc6c70f31d52c99299320cf81c3cb2c6b91ec7269414e0718a6d138e34/mypy-1.14.0-cp311-cp311-win_amd64.whl", hash = "sha256:7a52f26b9c9b1664a60d87675f3bae00b5c7f2806e0c2800545a32c325920bcc", size = 9780113 },
{ 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/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/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/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/f1/9c/e0f281b32d70c87b9e4d2939e302b1ff77ada4d7b0f2fb32890c144bc1d6/mypy-1.14.0-cp312-cp312-win_amd64.whl", hash = "sha256:585ed36031d0b3ee362e5107ef449a8b5dfd4e9c90ccbe36414ee405ee6b32ba", size = 9879477 },
{ 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/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/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/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/a7/40/fb4ad65d6d5f8c51396ecf6305ec0269b66013a5bf02d0e9528053640b4a/mypy-1.14.0-cp313-cp313-win_amd64.whl", hash = "sha256:e8c8387e5d9dff80e7daf961df357c80e694e942d9755f3ad77d69b0957b8e3f", size = 9888247 },
{ url = "https://files.pythonhosted.org/packages/39/32/0214608af400cdf8f5102144bb8af10d880675c65ed0b58f7e0e77175d50/mypy-1.14.0-py3-none-any.whl", hash = "sha256:2238d7f93fc4027ed1efc944507683df3ba406445a2b6c96e79666a045aadfab", size = 2752803 },
]
[[package]]
@ -6098,7 +6098,7 @@ name = "portalocker"
version = "2.10.1"
source = { registry = "https://pypi.org/simple" }
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 }
wheels = [
@ -7731,27 +7731,27 @@ wheels = [
[[package]]
name = "ruff"
version = "0.8.2"
version = "0.8.4"
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 = [
{ 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/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/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/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/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/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/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/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/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/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/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/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/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/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/0b/08/e8f519f61f1d624264bfd6b8829e4c5f31c3c61193bc3cff1f19dbe7626a/ruff-0.8.2-py3-none-win32.whl", hash = "sha256:ac42caaa0411d6a7d9594363294416e0e48fc1279e1b0e948391695db2b3d5b1", size = 8729396 },
{ 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/23/34/db20e12d3db11b8a2a8874258f0f6d96a9a4d631659d54575840557164c8/ruff-0.8.2-py3-none-win_arm64.whl", hash = "sha256:fb88e2a506b70cfbc2de6fae6681c4f944f7dd5f2fe87233a7233d888bad73e8", size = 9035131 },
{ 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/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/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/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/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/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/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/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/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/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/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/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/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/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/43/a9/1676ee9106995381e3d34bccac5bb28df70194167337ed4854c20f27c7ba/ruff-0.8.4-py3-none-win32.whl", hash = "sha256:552fb6d861320958ca5e15f28b20a3d071aa83b93caee33a87b471f99a6c0835", size = 8805621 },
{ 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/13/9f/026e18ca7d7766783d779dae5e9c656746c6ede36ef73c6d934aaf4a6dec/ruff-0.8.4-py3-none-win_arm64.whl", hash = "sha256:9183dd615d8df50defa8b1d9a074053891ba39025cf5ae88e8bcb52edcc4bf08", size = 9074500 },
]
[[package]]
@ -8575,19 +8575,19 @@ dependencies = [
{ name = "fsspec" },
{ name = "jinja2" },
{ name = "networkx" },
{ name = "nvidia-cublas-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" },
{ name = "nvidia-cuda-cupti-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" },
{ name = "nvidia-cuda-nvrtc-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" },
{ name = "nvidia-cuda-runtime-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" },
{ name = "nvidia-cudnn-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" },
{ name = "nvidia-cufft-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" },
{ name = "nvidia-curand-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" },
{ name = "nvidia-cusolver-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" },
{ name = "nvidia-cusparse-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" },
{ name = "nvidia-nccl-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" },
{ name = "nvidia-nvtx-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 sys_platform == '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 sys_platform == 'linux'" },
{ name = "nvidia-cudnn-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" },
{ name = "nvidia-cufft-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" },
{ name = "nvidia-curand-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" },
{ name = "nvidia-cusolver-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" },
{ name = "nvidia-cusparse-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" },
{ name = "nvidia-nccl-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" },
{ name = "nvidia-nvtx-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" },
{ 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" },
]
wheels = [
@ -8628,7 +8628,7 @@ name = "tqdm"
version = "4.67.1"
source = { registry = "https://pypi.org/simple" }
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 }
wheels = [