build: Bump ruff version to 0.9.7 (#6614)
* Bump ruff version to 0.9.7 * [autofix.ci] apply automated fixes * refactor: Update Gmail Agent starter project to Langflow 1.2.0 This commit updates the Gmail Agent starter project with new node IDs and configuration reflecting Langflow version 1.2.0. Changes include: - Updated node identifiers for Agent, ChatInput, ChatOutput, and ComposioAPI - Refreshed project metadata and version information - Minor adjustments to node configurations and viewport settings * [autofix.ci] apply automated fixes * fix: Revert Gmail Agent name to original * fix: Remove trailing comma in Gmail Agent JSON * feat: Add tags to Gmail Agent starter project --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: Gabriel Luiz Freitas Almeida <gabriel@langflow.org>
This commit is contained in:
parent
0108f2f475
commit
d9b8211a18
38 changed files with 214 additions and 207 deletions
2
.github/workflows/py_autofix.yml
vendored
2
.github/workflows/py_autofix.yml
vendored
|
|
@ -14,7 +14,7 @@ jobs:
|
|||
- uses: actions/checkout@v4
|
||||
- name: "Setup Environment"
|
||||
uses: ./.github/actions/setup-uv
|
||||
- run: uv run ruff check --fix-only . --ignore A005
|
||||
- run: uv run ruff check --fix-only .
|
||||
- run: uv run ruff format . --config pyproject.toml
|
||||
- uses: autofix-ci/action@551dded8c6cc8a1054039c8bc0b8b48c51dfc6ef
|
||||
- name: Minimize uv cache
|
||||
|
|
|
|||
2
.github/workflows/style-check-py.yml
vendored
2
.github/workflows/style-check-py.yml
vendored
|
|
@ -24,6 +24,6 @@ jobs:
|
|||
- name: Register problem matcher
|
||||
run: echo "::add-matcher::.github/workflows/matchers/ruff.json"
|
||||
- name: Run Ruff Check
|
||||
run: uv run --only-dev ruff check --output-format=github . --ignore A005
|
||||
run: uv run --only-dev ruff check --output-format=github .
|
||||
- name: Minimize uv cache
|
||||
run: uv cache prune --ci
|
||||
|
|
|
|||
2
Makefile
2
Makefile
|
|
@ -198,7 +198,7 @@ fix_codespell: ## run codespell to fix spelling errors
|
|||
poetry run codespell --toml pyproject.toml --write
|
||||
|
||||
format_backend: ## backend code formatters
|
||||
@uv run ruff check . --fix --ignore EXE002 --ignore A005
|
||||
@uv run ruff check . --fix
|
||||
@uv run ruff format . --config pyproject.toml
|
||||
|
||||
format_frontend: ## frontend code formatters
|
||||
|
|
|
|||
|
|
@ -121,7 +121,7 @@ dev = [
|
|||
"types-redis>=4.6.0.5",
|
||||
"ipykernel>=6.29.0",
|
||||
"mypy>=1.11.0",
|
||||
"ruff>=0.9.1,<0.10",
|
||||
"ruff>=0.9.7,<0.10",
|
||||
"httpx>=0.27.0",
|
||||
"pytest>=8.2.0",
|
||||
"types-requests>=2.32.0",
|
||||
|
|
|
|||
|
|
@ -580,14 +580,14 @@ async def experimental_run_flow(
|
|||
|
||||
|
||||
@router.post(
|
||||
"/predict/{flow_id}",
|
||||
"/predict/{_flow_id}",
|
||||
dependencies=[Depends(api_key_security)],
|
||||
)
|
||||
@router.post(
|
||||
"/process/{flow_id}",
|
||||
"/process/{_flow_id}",
|
||||
dependencies=[Depends(api_key_security)],
|
||||
)
|
||||
async def process() -> None:
|
||||
async def process(_flow_id) -> None:
|
||||
"""Endpoint to process an input with a given flow_id."""
|
||||
# Raise a depreciation warning
|
||||
logger.warning(
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
# noqa: A005
|
||||
|
|
@ -89,9 +89,9 @@ class ApifyActorsComponent(Component):
|
|||
|
||||
def run_model(self) -> list[Data]:
|
||||
"""Run the Actor and return node output."""
|
||||
_input = json.loads(self.run_input)
|
||||
input_ = json.loads(self.run_input)
|
||||
fields = ApifyActorsComponent.parse_dataset_fields(self.dataset_fields) if self.dataset_fields else None
|
||||
res = self._run_actor(self.actor_id, _input, fields=fields)
|
||||
res = self._run_actor(self.actor_id, input_, fields=fields)
|
||||
if self.flatten_dataset:
|
||||
res = [ApifyActorsComponent.flatten(item) for item in res]
|
||||
data = [Data(data=item) for item in res]
|
||||
|
|
@ -113,16 +113,16 @@ class ApifyActorsComponent(Component):
|
|||
properties = {"run_input": properties}
|
||||
|
||||
# works from input schema
|
||||
_info = [
|
||||
info_ = [
|
||||
(
|
||||
"JSON encoded as a string with input schema (STRICTLY FOLLOW JSON FORMAT AND SCHEMA):\n\n"
|
||||
f"{json.dumps(properties, separators=(',', ':'))}"
|
||||
)
|
||||
]
|
||||
if required:
|
||||
_info.append("\n\nRequired fields:\n" + "\n".join(required))
|
||||
info_.append("\n\nRequired fields:\n" + "\n".join(required))
|
||||
|
||||
info = "".join(_info)
|
||||
info = "".join(info_)
|
||||
|
||||
input_model_cls = ApifyActorsComponent.create_input_model_class(info)
|
||||
tool_cls = ApifyActorsComponent.create_tool_class(self, readme, input_model_cls, actor_id)
|
||||
|
|
|
|||
|
|
@ -158,7 +158,7 @@ class ComposioAPIComponent(LCToolComponent):
|
|||
|
||||
for item in data.get("items", []):
|
||||
for auth_scheme in item.get("auth_schemes", []):
|
||||
if auth_scheme.get("mode") in ["OAUTH1", "OAUTH2"]:
|
||||
if auth_scheme.get("mode") in {"OAUTH1", "OAUTH2"}:
|
||||
oauth_apps.append(item["key"].upper())
|
||||
break
|
||||
except requests.RequestException as e:
|
||||
|
|
|
|||
|
|
@ -321,7 +321,7 @@ class APIRequestComponent(Component):
|
|||
elif field_name == "curl":
|
||||
field_config["advanced"] = not use_curl
|
||||
field_config["real_time_refresh"] = use_curl
|
||||
elif field_name in ["body", "headers"]:
|
||||
elif field_name in {"body", "headers"}:
|
||||
field_config["advanced"] = True # Always keep body and headers in advanced when use_curl is False
|
||||
else:
|
||||
field_config["advanced"] = use_curl
|
||||
|
|
@ -359,7 +359,7 @@ class APIRequestComponent(Component):
|
|||
if field_name in common_fields:
|
||||
field_config["advanced"] = False
|
||||
elif field_name in body_fields:
|
||||
field_config["advanced"] = method not in ["POST", "PUT", "PATCH"]
|
||||
field_config["advanced"] = method not in {"POST", "PUT", "PATCH"}
|
||||
elif field_name in always_advanced_fields:
|
||||
field_config["advanced"] = True
|
||||
else:
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import operator
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
from loguru import logger
|
||||
|
|
@ -165,7 +166,7 @@ class BatchRunComponent(Component):
|
|||
]
|
||||
|
||||
# Sort by index to maintain order
|
||||
responses_with_idx.sort(key=lambda x: x[0])
|
||||
responses_with_idx.sort(key=operator.itemgetter(0))
|
||||
|
||||
# Build the final data with enhanced metadata
|
||||
rows: list[dict[str, Any]] = []
|
||||
|
|
|
|||
|
|
@ -138,7 +138,7 @@ class AnthropicModelComponent(LCModelComponent):
|
|||
return None
|
||||
|
||||
def update_build_config(self, build_config: dotdict, field_value: Any, field_name: str | None = None):
|
||||
if field_name in ("base_url", "model_name", "tool_model_enabled", "api_key") and field_value:
|
||||
if field_name in {"base_url", "model_name", "tool_model_enabled", "api_key"} and field_value:
|
||||
try:
|
||||
if len(self.api_key) == 0:
|
||||
ids = ANTHROPIC_MODELS
|
||||
|
|
|
|||
|
|
@ -129,7 +129,7 @@ class GoogleGenerativeAIComponent(LCModelComponent):
|
|||
return model_ids
|
||||
|
||||
def update_build_config(self, build_config: dotdict, field_value: Any, field_name: str | None = None):
|
||||
if field_name in ("base_url", "model_name", "tool_model_enabled", "api_key") and field_value:
|
||||
if field_name in {"base_url", "model_name", "tool_model_enabled", "api_key"} and field_value:
|
||||
try:
|
||||
if len(self.api_key) == 0:
|
||||
ids = GOOGLE_GENERATIVE_AI_MODELS
|
||||
|
|
|
|||
|
|
@ -105,7 +105,7 @@ class GroqModel(LCModelComponent):
|
|||
return model_ids
|
||||
|
||||
def update_build_config(self, build_config: dict, field_value: str, field_name: str | None = None):
|
||||
if field_name in ("base_url", "model_name", "tool_model_enabled", "api_key") and field_value:
|
||||
if field_name in {"base_url", "model_name", "tool_model_enabled", "api_key"} and field_value:
|
||||
try:
|
||||
if len(self.api_key) != 0:
|
||||
try:
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ class NVIDIAModelComponent(LCModelComponent):
|
|||
return [model.id for model in build_model.available_models]
|
||||
|
||||
def update_build_config(self, build_config: dotdict, field_value: Any, field_name: str | None = None):
|
||||
if field_name in ("base_url", "model_name", "tool_model_enabled", "api_key") and field_value:
|
||||
if field_name in {"base_url", "model_name", "tool_model_enabled", "api_key"} and field_value:
|
||||
try:
|
||||
ids = self.get_models(self.tool_model_enabled)
|
||||
build_config["model_name"]["options"] = ids
|
||||
|
|
|
|||
|
|
@ -231,5 +231,5 @@ class NvidiaIngestComponent(Component):
|
|||
# image is not yet supported; skip if encountered
|
||||
self.log(f"Unsupported document type: {document_type}", name="NVIDIAIngestComponent")
|
||||
|
||||
self.status = data if data else "No data"
|
||||
self.status = data or "No data"
|
||||
return data
|
||||
|
|
|
|||
|
|
@ -87,7 +87,7 @@ class SaveToFileComponent(Component):
|
|||
build_config["data"]["show"] = field_value == "Data"
|
||||
build_config["message"]["show"] = field_value == "Message"
|
||||
|
||||
if field_value in ["DataFrame", "Data"]:
|
||||
if field_value in {"DataFrame", "Data"}:
|
||||
build_config["file_format"]["options"] = self.DATA_FORMAT_CHOICES
|
||||
elif field_value == "Message":
|
||||
build_config["file_format"]["options"] = self.MESSAGE_FORMAT_CHOICES
|
||||
|
|
|
|||
|
|
@ -112,7 +112,7 @@ class ArXivComponent(Component):
|
|||
|
||||
# Validate URL scheme and host
|
||||
parsed_url = urlparse(url)
|
||||
if parsed_url.scheme not in ("http", "https"):
|
||||
if parsed_url.scheme not in {"http", "https"}:
|
||||
error_msg = f"Invalid URL scheme: {parsed_url.scheme}"
|
||||
raise ValueError(error_msg)
|
||||
if parsed_url.hostname != "export.arxiv.org":
|
||||
|
|
|
|||
|
|
@ -571,7 +571,7 @@ class AstraDBVectorStoreComponent(LCVectorStoreComponent):
|
|||
# Go over each possible provider and add metadata to configure in Astra DB Portal
|
||||
for provider in provider_options:
|
||||
# Skip Bring your own and Nvidia, automatically configured
|
||||
if provider in ["Bring your own", "Nvidia"]:
|
||||
if provider in {"Bring your own", "Nvidia"}:
|
||||
build_config["collection_name"]["dialog_inputs"]["fields"]["data"]["node"]["template"][
|
||||
"embedding_generation_provider"
|
||||
]["options_metadata"].append({"icon": self.get_provider_icon(provider_name=provider.lower())})
|
||||
|
|
@ -601,7 +601,7 @@ class AstraDBVectorStoreComponent(LCVectorStoreComponent):
|
|||
# If we retrieved options based on the token, show the dropdown
|
||||
build_config["collection_name"]["options"] = [col["name"] for col in collection_options]
|
||||
build_config["collection_name"]["options_metadata"] = [
|
||||
{k: v for k, v in col.items() if k not in ["name"]} for col in collection_options
|
||||
{k: v for k, v in col.items() if k != "name"} for col in collection_options
|
||||
]
|
||||
|
||||
# Reset the selected collection
|
||||
|
|
@ -620,7 +620,7 @@ class AstraDBVectorStoreComponent(LCVectorStoreComponent):
|
|||
# If we retrieved options based on the token, show the dropdown
|
||||
build_config["database_name"]["options"] = [db["name"] for db in database_options]
|
||||
build_config["database_name"]["options_metadata"] = [
|
||||
{k: v for k, v in db.items() if k not in ["name"]} for db in database_options
|
||||
{k: v for k, v in db.items() if k != "name"} for db in database_options
|
||||
]
|
||||
|
||||
# Reset the selected database
|
||||
|
|
@ -667,12 +667,8 @@ class AstraDBVectorStoreComponent(LCVectorStoreComponent):
|
|||
raise ValueError(msg) from e
|
||||
|
||||
# Add the new database to the list of options
|
||||
build_config["database_name"]["options"] = build_config["database_name"]["options"] + [
|
||||
field_value["new_database_name"]
|
||||
]
|
||||
build_config["database_name"]["options_metadata"] = build_config["database_name"]["options_metadata"] + [
|
||||
{"status": "PENDING"}
|
||||
]
|
||||
build_config["database_name"]["options"] += [field_value["new_database_name"]]
|
||||
build_config["database_name"]["options_metadata"] += [{"status": "PENDING"}]
|
||||
|
||||
return self.reset_collection_list(build_config)
|
||||
|
||||
|
|
@ -726,9 +722,9 @@ class AstraDBVectorStoreComponent(LCVectorStoreComponent):
|
|||
|
||||
# Add the new collection to the list of options
|
||||
icon = "NVIDIA" if provider == "Nvidia" else "vectorstores"
|
||||
build_config["collection_name"]["options_metadata"] = build_config["collection_name"][
|
||||
"options_metadata"
|
||||
] + [{"records": 0, "provider": provider, "icon": icon, "model": model}]
|
||||
build_config["collection_name"]["options_metadata"] += [
|
||||
{"records": 0, "provider": provider, "icon": icon, "model": model}
|
||||
]
|
||||
|
||||
return build_config
|
||||
|
||||
|
|
@ -748,7 +744,7 @@ class AstraDBVectorStoreComponent(LCVectorStoreComponent):
|
|||
return self.reset_build_config(build_config)
|
||||
|
||||
# If this is the first execution of the component, reset and build database list
|
||||
if first_run or field_name in ["token", "environment"]:
|
||||
if first_run or field_name in {"token", "environment"}:
|
||||
return self.reset_database_list(build_config)
|
||||
|
||||
# Refresh the collection name options
|
||||
|
|
@ -790,7 +786,12 @@ class AstraDBVectorStoreComponent(LCVectorStoreComponent):
|
|||
# Add the new collection to the list of options
|
||||
build_config["collection_name"]["options"].append(field_value)
|
||||
build_config["collection_name"]["options_metadata"].append(
|
||||
{"records": 0, "provider": None, "icon": "", "model": None}
|
||||
{
|
||||
"records": 0,
|
||||
"provider": None,
|
||||
"icon": "",
|
||||
"model": None,
|
||||
}
|
||||
)
|
||||
|
||||
# Ensure that autodetect collection is set to False, since its a new collection
|
||||
|
|
|
|||
|
|
@ -230,7 +230,7 @@ class YouTubeVideoDetailsComponent(Component):
|
|||
thumb_cols = [col for col in video_df.columns if col.startswith("thumbnail_")]
|
||||
|
||||
# Reorder columns based on what's included
|
||||
ordered_cols = basic_cols[:]
|
||||
ordered_cols = basic_cols.copy()
|
||||
|
||||
if self.include_statistics:
|
||||
ordered_cols.extend([col for col in stat_cols if col in video_df.columns])
|
||||
|
|
|
|||
|
|
@ -518,7 +518,7 @@ def layered_topological_sort(
|
|||
|
||||
layers: list[list[str]] = []
|
||||
visited = set()
|
||||
cycle_counts = {vertex: 0 for vertex in vertices_ids}
|
||||
cycle_counts = dict.fromkeys(vertices_ids, 0)
|
||||
current_layer = 0
|
||||
|
||||
# Process the first layer separately to avoid duplicates
|
||||
|
|
|
|||
|
|
@ -322,7 +322,7 @@ def json_schema_from_flow(flow: Flow) -> dict:
|
|||
from langflow.graph.graph.base import Graph
|
||||
|
||||
# Get the flow's data which contains the nodes and their configurations
|
||||
flow_data = flow.data if flow.data else {}
|
||||
flow_data = flow.data or {}
|
||||
|
||||
graph = Graph.from_payload(flow_data)
|
||||
input_nodes = [vertex for vertex in graph.vertices if vertex.is_input]
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -1,3 +1,4 @@
|
|||
# noqa: A005
|
||||
from langflow.inputs import (
|
||||
BoolInput,
|
||||
CodeInput,
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
# noqa: A005
|
||||
from .logger import configure, logger
|
||||
from .setup import disable_logging, enable_logging
|
||||
|
||||
|
|
|
|||
|
|
@ -145,7 +145,7 @@ def _serialize_numpy_type(obj: Any, max_length: int | None, max_items: int | Non
|
|||
if np.issubdtype(obj.dtype, np.bool_):
|
||||
return bool(obj)
|
||||
if np.issubdtype(obj.dtype, np.complexfloating):
|
||||
return complex(cast(complex, obj))
|
||||
return complex(cast("complex", obj))
|
||||
if np.issubdtype(obj.dtype, np.str_):
|
||||
return _serialize_str(str(obj), max_length, max_items)
|
||||
if np.issubdtype(obj.dtype, np.bytes_) and hasattr(obj, "tobytes"):
|
||||
|
|
@ -209,7 +209,7 @@ def _serialize_dispatcher(obj: Any, max_length: int | None, max_items: int | Non
|
|||
if np.issubdtype(obj.dtype, np.bool_):
|
||||
return bool(obj)
|
||||
if np.issubdtype(obj.dtype, np.complexfloating):
|
||||
return complex(cast(complex, obj))
|
||||
return complex(cast("complex", obj))
|
||||
if np.issubdtype(obj.dtype, np.str_):
|
||||
return str(obj)
|
||||
if np.issubdtype(obj.dtype, np.bytes_) and hasattr(obj, "tobytes"):
|
||||
|
|
|
|||
|
|
@ -203,7 +203,7 @@ class JobQueueService(Service):
|
|||
return
|
||||
|
||||
logger.info(f"Commencing cleanup for job_id {job_id}")
|
||||
main_queue, event_manager, task = self._queues[job_id]
|
||||
main_queue, _event_manager, task = self._queues[job_id]
|
||||
|
||||
# Cancel the associated task if it is still running.
|
||||
if task and not task.done():
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
# noqa: A005
|
||||
|
|
@ -93,7 +93,7 @@ class TelemetryService(Service):
|
|||
|
||||
def _get_langflow_desktop(self) -> bool:
|
||||
# Coerce to bool, could be 1, 0, True, False, "1", "0", "True", "False"
|
||||
return str(os.getenv("LANGFLOW_DESKTOP", "False")).lower() in ("1", "true")
|
||||
return str(os.getenv("LANGFLOW_DESKTOP", "False")).lower() in {"1", "true"}
|
||||
|
||||
async def log_package_version(self) -> None:
|
||||
python_version = ".".join(platform.python_version().split(".")[:2])
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ async def test_update_component_outputs(client: AsyncClient, logged_in_headers:
|
|||
async def test_update_component_model_name_options(client: AsyncClient, logged_in_headers: dict):
|
||||
"""Test that model_name options are updated when selecting a provider."""
|
||||
component = AgentComponent()
|
||||
component_node, cc_instance = build_custom_component_template(
|
||||
component_node, _cc_instance = build_custom_component_template(
|
||||
component,
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -271,9 +271,9 @@ class TestChromaVectorStoreComponent(ComponentTestBaseWithoutClient):
|
|||
assert isinstance(data_obj, Data)
|
||||
assert "id" in data_obj.data
|
||||
assert "text" in data_obj.data
|
||||
assert data_obj.data["text"] in ["Document 1", "Document 2"]
|
||||
assert data_obj.data["text"] in {"Document 1", "Document 2"}
|
||||
assert "metadata_field" in data_obj.data
|
||||
assert data_obj.data["metadata_field"] in ["value1", "value2"]
|
||||
assert data_obj.data["metadata_field"] in {"value1", "value2"}
|
||||
|
||||
def test_chroma_collection_to_data_without_metadata(
|
||||
self, component_class: type[ChromaVectorStoreComponent], default_kwargs: dict[str, Any]
|
||||
|
|
@ -300,7 +300,7 @@ class TestChromaVectorStoreComponent(ComponentTestBaseWithoutClient):
|
|||
assert isinstance(data_obj, Data)
|
||||
assert "id" in data_obj.data
|
||||
assert "text" in data_obj.data
|
||||
assert data_obj.data["text"] in ["Simple document 1", "Simple document 2"]
|
||||
assert data_obj.data["text"] in {"Simple document 1", "Simple document 2"}
|
||||
|
||||
def test_chroma_collection_to_data_empty_collection(
|
||||
self, component_class: type[ChromaVectorStoreComponent], default_kwargs: dict[str, Any]
|
||||
|
|
|
|||
|
|
@ -869,7 +869,7 @@ def test_get_sorted_vertices_with_unconnected_graph():
|
|||
predecessor_map = {vertex: data["predecessors"] for vertex, data in graph_dict.items()}
|
||||
|
||||
def is_input_vertex(vertex_id: str) -> bool:
|
||||
return vertex_id in ["A"]
|
||||
return vertex_id == "A"
|
||||
|
||||
def get_vertex_predecessors(vertex_id: str) -> list[str]:
|
||||
return predecessor_map[vertex_id]
|
||||
|
|
|
|||
|
|
@ -263,13 +263,13 @@ class TestSerializationHypothesis:
|
|||
assert isinstance(serialize(np.uint64(42)), int)
|
||||
|
||||
# Test floats
|
||||
assert serialize(np.float64(3.14)) == 3.14
|
||||
assert isinstance(serialize(np.float64(3.14)), float)
|
||||
assert serialize(np.float64(math.pi)) == math.pi
|
||||
assert isinstance(serialize(np.float64(math.pi)), float)
|
||||
|
||||
# Test float32 (need to account for precision differences)
|
||||
float32_val = serialize(np.float32(3.14))
|
||||
float32_val = serialize(np.float32(math.pi))
|
||||
assert isinstance(float32_val, float)
|
||||
assert abs(float32_val - 3.14) < 1e-6 # Check if close enough
|
||||
assert abs(float32_val - math.pi) < 1e-6 # Check if close enough
|
||||
|
||||
# Test bool
|
||||
assert serialize(np.bool_(True)) is True # noqa: FBT003
|
||||
|
|
|
|||
2
uv.lock
generated
2
uv.lock
generated
|
|
@ -4544,7 +4544,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.9.1,<0.10" },
|
||||
{ name = "ruff", specifier = ">=0.9.7,<0.10" },
|
||||
{ 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" },
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue