diff --git a/.changeset/chubby-hairs-beam.md b/.changeset/chubby-hairs-beam.md new file mode 100644 index 0000000000..78e3986753 --- /dev/null +++ b/.changeset/chubby-hairs-beam.md @@ -0,0 +1,6 @@ +--- +"gradio": minor +"gradio_client": minor +--- + +feat:Drop python 3.8 and 3.9 diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 7ec3127cc3..33a2c936d0 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -25,6 +25,7 @@ jobs: uses: "gradio-app/gradio/.github/actions/install-all-deps@main" with: always_install_pnpm: true + python_version: "3.10" skip_build: "false" - name: Build packages run: | diff --git a/.github/workflows/storybook-build.yml b/.github/workflows/storybook-build.yml index 31cf484b11..2319a12822 100644 --- a/.github/workflows/storybook-build.yml +++ b/.github/workflows/storybook-build.yml @@ -52,6 +52,7 @@ jobs: - name: install dependencies uses: "gradio-app/gradio/.github/actions/install-all-deps@main" with: + python_version: "3.10" always_install_pnpm: true skip_build: "true" - name: build client diff --git a/.github/workflows/test-functional-lite.yml b/.github/workflows/test-functional-lite.yml index f9b7d6d387..6731594ecd 100644 --- a/.github/workflows/test-functional-lite.yml +++ b/.github/workflows/test-functional-lite.yml @@ -49,6 +49,8 @@ jobs: always_install_pnpm: true build_lite: true skip_build: true + python_version: "3.10" + test: true - run: pnpm exec playwright install chromium firefox - name: Run Lite E2E tests run: | diff --git a/.github/workflows/test-functional.yml b/.github/workflows/test-functional.yml index 4812b5f3ab..3cd4a13f71 100644 --- a/.github/workflows/test-functional.yml +++ b/.github/workflows/test-functional.yml @@ -47,7 +47,9 @@ jobs: uses: "gradio-app/gradio/.github/actions/install-all-deps@main" with: always_install_pnpm: true - - name: install outbreak_forecast dependencies + python_version: "3.10" + test: true + - name: install dependencies for specific tests run: | . venv/bin/activate python -m pip install -r demo/outbreak_forecast/requirements.txt diff --git a/.github/workflows/website-build.yml b/.github/workflows/website-build.yml index d4a2cfa4b7..87594bcc04 100644 --- a/.github/workflows/website-build.yml +++ b/.github/workflows/website-build.yml @@ -56,6 +56,7 @@ jobs: with: always_install_pnpm: true skip_build: true + python_version: "3.10" - name: build client run: pnpm --filter @gradio/client build diff --git a/.github/workflows/website-docs-build.yml b/.github/workflows/website-docs-build.yml index 7552b77a52..69a43d30e2 100644 --- a/.github/workflows/website-docs-build.yml +++ b/.github/workflows/website-docs-build.yml @@ -51,7 +51,9 @@ jobs: - name: install dependencies uses: "gradio-app/gradio/.github/actions/install-all-deps@main" with: + python_version: "3.10" skip_build: true + test: true # generated when installing deps - name: upload website json artifacts diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 47d8976c17..28ea87702f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -7,7 +7,7 @@ More than 200 awesome developers have contributed to the `gradio` library, and w Prerequisites: -- [Python 3.8+](https://www.python.org/downloads/) +- [Python 3.10+](https://www.python.org/downloads/) - [Node.js v16.14+](https://nodejs.dev/en/download/package-manager/) (only needed if you are making changes to the frontend) - [pnpm 8.1+](https://pnpm.io/8.x/installation) (only needed if you are making changes to the frontend) diff --git a/client/python/gradio_client/cli/deploy_discord.py b/client/python/gradio_client/cli/deploy_discord.py index 9a828a32a0..e653007428 100644 --- a/client/python/gradio_client/cli/deploy_discord.py +++ b/client/python/gradio_client/cli/deploy_discord.py @@ -1,7 +1,6 @@ -from typing import List, Optional +from typing import Annotated, Optional from typer import Option -from typing_extensions import Annotated from gradio_client import Client @@ -17,7 +16,7 @@ def main( str, Option(help="Discord bot token. Get one on the discord website.") ] = None, api_names: Annotated[ - List[str], Option(help="Api names to turn into discord bots") + list[str], Option(help="Api names to turn into discord bots") ] = None, to_id: Annotated[ Optional[str], Option(help="Name of the space used to host the discord bot") diff --git a/client/python/gradio_client/client.py b/client/python/gradio_client/client.py index 2d7ce6c0a1..8cb81e327c 100644 --- a/client/python/gradio_client/client.py +++ b/client/python/gradio_client/client.py @@ -16,13 +16,14 @@ import urllib.parse import uuid import warnings +from collections.abc import Callable from concurrent.futures import Future from dataclasses import dataclass from datetime import datetime from functools import partial from pathlib import Path from threading import Lock -from typing import Any, Callable, Literal +from typing import Any, Literal import httpx import huggingface_hub @@ -1303,7 +1304,11 @@ def download_files(self, *data) -> tuple: def remove_skipped_components(self, *data) -> tuple: """""" - data = [d for d, oct in zip(data, self.output_component_types) if not oct.skip] + data = [ + d + for d, oct in zip(data, self.output_component_types, strict=False) + if not oct.skip + ] return tuple(data) def reduce_singleton_output(self, *data) -> Any: diff --git a/client/python/gradio_client/compatibility.py b/client/python/gradio_client/compatibility.py index ca5f323b74..8348cf0c19 100644 --- a/client/python/gradio_client/compatibility.py +++ b/client/python/gradio_client/compatibility.py @@ -172,7 +172,7 @@ def _upload( "orig_name": Path(f).name, "data": None, } - for f, o in zip(fs, output) + for f, o in zip(fs, output, strict=False) ] else: o = next(o for ix, o in enumerate(result) if indices[ix] == i) @@ -207,7 +207,7 @@ def insert_state(self, *data) -> tuple: def remove_skipped_components(self, *data) -> tuple: data = [ d - for d, oct in zip(data, self.output_component_types) + for d, oct in zip(data, self.output_component_types, strict=False) if oct not in utils.SKIP_COMPONENTS ] return tuple(data) @@ -235,13 +235,15 @@ def serialize(self, *data) -> tuple: files = [ f - for f, t in zip(data, self.input_component_types) + for f, t in zip(data, self.input_component_types, strict=False) if t in ["file", "uploadbutton"] ] uploaded_files = self._upload(files) data = list(data) self._add_uploaded_files_to_data(uploaded_files, data) - o = tuple([s.serialize(d) for s, d in zip(self.serializers, data)]) + o = tuple( + [s.serialize(d) for s, d in zip(self.serializers, data, strict=False)] + ) return o def deserialize(self, *data) -> tuple: @@ -257,7 +259,7 @@ def deserialize(self, *data) -> tuple: hf_token=self.client.hf_token, root_url=self.root_url, ) - for s, d in zip(self.deserializers, data) + for s, d in zip(self.deserializers, data, strict=False) ] ) return outputs diff --git a/client/python/gradio_client/documentation.py b/client/python/gradio_client/documentation.py index 741ad78abb..a3ab654930 100644 --- a/client/python/gradio_client/documentation.py +++ b/client/python/gradio_client/documentation.py @@ -6,8 +6,8 @@ import inspect import warnings from collections import defaultdict +from collections.abc import Callable from functools import lru_cache -from typing import Callable classes_to_document = defaultdict(list) classes_inherit_documentation = {} diff --git a/client/python/gradio_client/utils.py b/client/python/gradio_client/utils.py index 1e5cc88c4c..ce08e0de24 100644 --- a/client/python/gradio_client/utils.py +++ b/client/python/gradio_client/utils.py @@ -13,12 +13,13 @@ import tempfile import time import warnings +from collections.abc import Callable, Coroutine from dataclasses import dataclass, field from datetime import datetime from enum import Enum from pathlib import Path from threading import Lock -from typing import TYPE_CHECKING, Any, Callable, Coroutine, Literal, Optional, TypedDict +from typing import TYPE_CHECKING, Any, Literal, Optional, TypedDict import fsspec.asyn import httpx diff --git a/client/python/pyproject.toml b/client/python/pyproject.toml index 54778eb45e..c7dcf0846f 100644 --- a/client/python/pyproject.toml +++ b/client/python/pyproject.toml @@ -7,7 +7,7 @@ name = "gradio_client" dynamic = ["version", "dependencies", "readme"] description = "Python library for easily interacting with trained machine learning models" license = "Apache-2.0" -requires-python = ">=3.8" +requires-python = ">=3.10" authors = [ { name = "Abubakar Abid", email = "gradio-team@huggingface.co" }, { name = "Ali Abid", email = "gradio-team@huggingface.co" }, diff --git a/gradio/_simple_templates/simpledropdown.py b/gradio/_simple_templates/simpledropdown.py index c1d8ad1423..3fbe8cebd9 100644 --- a/gradio/_simple_templates/simpledropdown.py +++ b/gradio/_simple_templates/simpledropdown.py @@ -1,7 +1,8 @@ from __future__ import annotations import warnings -from typing import TYPE_CHECKING, Any, Callable, Sequence +from collections.abc import Callable, Sequence +from typing import TYPE_CHECKING, Any from gradio.components.base import Component, FormComponent from gradio.events import Events diff --git a/gradio/_simple_templates/simpleimage.py b/gradio/_simple_templates/simpleimage.py index 712b843f60..9e33732178 100644 --- a/gradio/_simple_templates/simpleimage.py +++ b/gradio/_simple_templates/simpleimage.py @@ -2,8 +2,9 @@ from __future__ import annotations +from collections.abc import Sequence from pathlib import Path -from typing import TYPE_CHECKING, Any, Sequence +from typing import TYPE_CHECKING, Any from gradio_client import handle_file from gradio_client.documentation import document diff --git a/gradio/_simple_templates/simpletextbox.py b/gradio/_simple_templates/simpletextbox.py index 12aa16acb5..fb0eb4209a 100644 --- a/gradio/_simple_templates/simpletextbox.py +++ b/gradio/_simple_templates/simpletextbox.py @@ -1,6 +1,7 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Any, Callable, Sequence +from collections.abc import Callable, Sequence +from typing import TYPE_CHECKING, Any from gradio.components.base import Component, FormComponent from gradio.events import Events diff --git a/gradio/blocks.py b/gradio/blocks.py index f9ce3b6dc2..864c27fbcd 100644 --- a/gradio/blocks.py +++ b/gradio/blocks.py @@ -15,16 +15,13 @@ import warnings import webbrowser from collections import defaultdict +from collections.abc import AsyncIterator, Callable, Sequence, Set from pathlib import Path from types import ModuleType from typing import ( TYPE_CHECKING, - AbstractSet, Any, - AsyncIterator, - Callable, Literal, - Sequence, cast, ) from urllib.parse import urlparse, urlunparse @@ -681,14 +678,14 @@ def set_event_trigger( Component | BlockContext | Sequence[Component | BlockContext] - | AbstractSet[Component | BlockContext] + | Set[Component | BlockContext] | None ), outputs: ( Component | BlockContext | Sequence[Component | BlockContext] - | AbstractSet[Component | BlockContext] + | Set[Component | BlockContext] | None ), preprocess: bool = True, @@ -754,7 +751,7 @@ def set_event_trigger( ) for target in targets ] - if isinstance(inputs, AbstractSet): + if isinstance(inputs, Set): inputs_as_dict = True inputs = sorted(inputs, key=lambda x: x._id) else: @@ -764,7 +761,7 @@ def set_event_trigger( elif not isinstance(inputs, Sequence): inputs = [inputs] - if isinstance(outputs, AbstractSet): + if isinstance(outputs, Set): outputs = sorted(outputs, key=lambda x: x._id) elif outputs is None: outputs = [] @@ -1215,7 +1212,7 @@ def iterate_over_children(children_list): raise ValueError( "This config is missing the 'dependencies' field and cannot be loaded." ) - for dependency, fn in zip(config["dependencies"], fns): + for dependency, fn in zip(config["dependencies"], fns, strict=False): # We used to add a "fake_event" to the config to cache examples # without removing it. This was causing bugs in calling gr.load # We fixed the issue by removing "fake_event" from the config in examples.py @@ -1521,7 +1518,9 @@ async def call_function( if iterator is None: # If not a generator function that has already run if block_fn.inputs_as_dict: - processed_input = [dict(zip(block_fn.inputs, processed_input))] + processed_input = [ + dict(zip(block_fn.inputs, processed_input, strict=False)) + ] processed_input, progress_index, _ = special_args( block_fn.fn, processed_input, request, event_data @@ -1928,11 +1927,11 @@ async def process_api( ) inputs = [ await self.preprocess_data(block_fn, list(i), state, explicit_call) - for i in zip(*inputs) + for i in zip(*inputs, strict=False) ] result = await self.call_function( block_fn, - list(zip(*inputs)), + list(zip(*inputs, strict=False)), None, request, event_id, @@ -1943,11 +1942,11 @@ async def process_api( preds = result["prediction"] data = [ await self.postprocess_data(block_fn, list(o), state) - for o in zip(*preds) + for o in zip(*preds, strict=False) ] if root_path is not None: data = processing_utils.add_root_url(data, root_path, None) # type: ignore - data = list(zip(*data)) + data = list(zip(*data, strict=False)) is_generating, iterator = None, None else: old_iterator = iterator @@ -1972,7 +1971,9 @@ async def process_api( if state: changed_state_ids = [ state_id - for hash_value, state_id in zip(hashed_values, state_ids_to_track) + for hash_value, state_id in zip( + hashed_values, state_ids_to_track, strict=False + ) if hash_value != utils.deep_hash(state[state_id]) ] diff --git a/gradio/chat_interface.py b/gradio/chat_interface.py index 38f4ab9aff..2ddceeca24 100644 --- a/gradio/chat_interface.py +++ b/gradio/chat_interface.py @@ -8,7 +8,8 @@ import functools import inspect import warnings -from typing import AsyncGenerator, Callable, Literal, Union, cast +from collections.abc import AsyncGenerator, Callable +from typing import Literal, Union, cast import anyio from gradio_client.documentation import document diff --git a/gradio/cli/commands/components/_docs_utils.py b/gradio/cli/commands/components/_docs_utils.py index 855e15eb07..9b61b76889 100644 --- a/gradio/cli/commands/components/_docs_utils.py +++ b/gradio/cli/commands/components/_docs_utils.py @@ -145,31 +145,14 @@ def get_type_arguments(type_hint) -> tuple: def get_container_name(arg): """Gets a human readable name for a type.""" - - # This is a bit of a hack to get the generic type for python 3.8 - typing_genericalias = getattr(typing, "_GenericAlias", None) - types_genericalias = getattr(types, "GenericAlias", None) - types_uniontype = getattr(types, "UnionType", None) - if types_genericalias is None: - raise ValueError( - """Unable to find GenericAlias type. This is likely because you are using an older version of python. Please upgrade to python 3.10 or higher.""" - ) - - generic_type_tuple = ( - (types_genericalias,) - if typing_genericalias is None - else (types_genericalias, typing_genericalias) - ) - if inspect.isclass(arg): return arg.__name__ - if isinstance(arg, (generic_type_tuple)): + if isinstance(arg, types.GenericAlias): return arg.__origin__.__name__ - elif types_uniontype and isinstance(arg, types_uniontype): + elif isinstance(arg, types.UnionType): return "Union" elif getattr(arg, "__origin__", None) is typing.Literal: return "Literal" - else: return str(arg) diff --git a/gradio/cli/commands/components/build.py b/gradio/cli/commands/components/build.py index 3c0bfed5e1..0cefe66824 100644 --- a/gradio/cli/commands/components/build.py +++ b/gradio/cli/commands/components/build.py @@ -4,12 +4,11 @@ import shutil import subprocess from pathlib import Path -from typing import Optional +from typing import Annotated, Optional import semantic_version import typer from tomlkit import dump, parse -from typing_extensions import Annotated import gradio from gradio.analytics import custom_component_analytics diff --git a/gradio/cli/commands/components/create.py b/gradio/cli/commands/components/create.py index 59453e9173..410c1ddde8 100644 --- a/gradio/cli/commands/components/create.py +++ b/gradio/cli/commands/components/create.py @@ -2,14 +2,13 @@ import shutil from pathlib import Path -from typing import Optional +from typing import Annotated, Optional import typer from rich import print from rich.panel import Panel from rich.prompt import Confirm, Prompt from tomlkit import dump, parse -from typing_extensions import Annotated from gradio.analytics import custom_component_analytics @@ -156,14 +155,14 @@ def _create( pyproject_toml["project"]["license"] = license_ # type: ignore requires_python = Prompt.ask( - "\n:snake: Please enter the [bold][magenta]allowed python[/][/] versions for your component. Leave blank for '>=3.8'" + "\n:snake: Please enter the [bold][magenta]allowed python[/][/] versions for your component. Leave blank for '>=3.10'" ) - requires_python = requires_python or ">=3.8" + requires_python = requires_python or ">=3.10" print( f":snake: Using requires-python of [bold][magenta]{requires_python}[/][/]" ) pyproject_toml["project"]["requires-python"] = ( # type: ignore - requires_python or ">=3.8" + requires_python or ">=3.10" ) print( diff --git a/gradio/cli/commands/components/dev.py b/gradio/cli/commands/components/dev.py index 0563618500..025c5ea320 100644 --- a/gradio/cli/commands/components/dev.py +++ b/gradio/cli/commands/components/dev.py @@ -3,11 +3,10 @@ import shutil import subprocess from pathlib import Path -from typing import Optional +from typing import Annotated, Optional import typer from rich import print -from typing_extensions import Annotated import gradio from gradio.analytics import custom_component_analytics diff --git a/gradio/cli/commands/components/docs.py b/gradio/cli/commands/components/docs.py index 62e0c068c1..145d9073ab 100644 --- a/gradio/cli/commands/components/docs.py +++ b/gradio/cli/commands/components/docs.py @@ -3,12 +3,11 @@ import importlib import re from pathlib import Path -from typing import Any, Optional +from typing import Annotated, Any, Optional import requests import tomlkit as toml from typer import Argument, Option -from typing_extensions import Annotated from gradio.analytics import custom_component_analytics from gradio.cli.commands.display import LivePanelDisplay @@ -201,5 +200,5 @@ def run_command( if type_mode == "simple": live.update( - "\n:orange_circle: [red]The docs were generated in simple mode. Updating python to a version greater than 3.9 will result in richer documentation.[/]" + "\n:orange_circle: [red]The docs were generated in simple mode. Updating python to a more recent version will result in richer documentation.[/]" ) diff --git a/gradio/cli/commands/components/install_component.py b/gradio/cli/commands/components/install_component.py index 08092e76d7..6dd0a90573 100644 --- a/gradio/cli/commands/components/install_component.py +++ b/gradio/cli/commands/components/install_component.py @@ -3,11 +3,10 @@ import shutil import subprocess from pathlib import Path -from typing import Optional +from typing import Annotated, Optional from rich.markup import escape from typer import Argument, Option -from typing_extensions import Annotated from gradio.cli.commands.display import LivePanelDisplay from gradio.utils import set_directory diff --git a/gradio/cli/commands/components/publish.py b/gradio/cli/commands/components/publish.py index ff2bdc8d71..2affbe4172 100644 --- a/gradio/cli/commands/components/publish.py +++ b/gradio/cli/commands/components/publish.py @@ -4,7 +4,7 @@ import shutil import tempfile from pathlib import Path -from typing import List, Optional +from typing import Annotated, Optional import semantic_version from huggingface_hub import HfApi @@ -14,7 +14,6 @@ from rich.prompt import Confirm, Prompt from tomlkit import parse from typer import Argument, Option -from typing_extensions import Annotated from gradio.analytics import custom_component_analytics @@ -37,7 +36,7 @@ def _get_version_from_file(dist_file: Path) -> Optional[str]: return match.group(1) -def _get_max_version(distribution_files: List[Path]) -> Optional[str]: +def _get_max_version(distribution_files: list[Path]) -> Optional[str]: versions = [] for p in distribution_files: version = _get_version_from_file(p) diff --git a/gradio/cli/commands/deploy_space.py b/gradio/cli/commands/deploy_space.py index ad082d5817..e3f19a26bd 100644 --- a/gradio/cli/commands/deploy_space.py +++ b/gradio/cli/commands/deploy_space.py @@ -2,12 +2,11 @@ import os import re -from typing import Optional +from typing import Annotated, Optional import huggingface_hub from rich import print from typer import Option -from typing_extensions import Annotated import gradio as gr diff --git a/gradio/cli/commands/reload.py b/gradio/cli/commands/reload.py index 2468c74d43..5df9995980 100644 --- a/gradio/cli/commands/reload.py +++ b/gradio/cli/commands/reload.py @@ -16,7 +16,7 @@ import sys import threading from pathlib import Path -from typing import List, Optional +from typing import Optional import typer from rich import print @@ -106,7 +106,7 @@ def _setup_config( def main( demo_path: Path, demo_name: str = "demo", - watch_dirs: Optional[List[str]] = None, + watch_dirs: Optional[list[str]] = None, encoding: str = "utf-8", ): # default execution pattern to start the server and watch changes diff --git a/gradio/components/annotated_image.py b/gradio/components/annotated_image.py index 0cd79082f0..111743605e 100644 --- a/gradio/components/annotated_image.py +++ b/gradio/components/annotated_image.py @@ -2,7 +2,8 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Any, List, Sequence +from collections.abc import Sequence +from typing import TYPE_CHECKING, Any import gradio_client.utils as client_utils import numpy as np @@ -28,7 +29,7 @@ class Annotation(GradioModel): class AnnotatedImageData(GradioModel): image: FileData - annotations: List[Annotation] + annotations: list[Annotation] @document() diff --git a/gradio/components/audio.py b/gradio/components/audio.py index 6b8abd327f..34f7f3986c 100644 --- a/gradio/components/audio.py +++ b/gradio/components/audio.py @@ -4,8 +4,9 @@ import dataclasses import io +from collections.abc import Callable, Sequence from pathlib import Path -from typing import TYPE_CHECKING, Any, Callable, Literal, Sequence +from typing import TYPE_CHECKING, Any, Literal import anyio import httpx diff --git a/gradio/components/bar_plot.py b/gradio/components/bar_plot.py index c38422c389..f2adef960d 100644 --- a/gradio/components/bar_plot.py +++ b/gradio/components/bar_plot.py @@ -3,7 +3,8 @@ from __future__ import annotations import warnings -from typing import TYPE_CHECKING, Any, Callable, Literal, Sequence +from collections.abc import Callable, Sequence +from typing import TYPE_CHECKING, Any, Literal from gradio_client.documentation import document diff --git a/gradio/components/base.py b/gradio/components/base.py index 12690b2ba1..7606ae85d2 100644 --- a/gradio/components/base.py +++ b/gradio/components/base.py @@ -10,9 +10,10 @@ import sys import warnings from abc import ABC, abstractmethod +from collections.abc import Callable, Sequence from enum import Enum from pathlib import Path -from typing import TYPE_CHECKING, Any, Callable, Sequence, Type +from typing import TYPE_CHECKING, Any import gradio_client.utils as client_utils @@ -172,7 +173,7 @@ def __init__( # This gets overridden when `select` is called self._selectable = False if not hasattr(self, "data_model"): - self.data_model: Type[GradioDataModel] | None = None + self.data_model: type[GradioDataModel] | None = None Block.__init__( self, diff --git a/gradio/components/button.py b/gradio/components/button.py index 1b42620018..e53901092a 100644 --- a/gradio/components/button.py +++ b/gradio/components/button.py @@ -2,7 +2,8 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Any, Callable, Literal, Sequence +from collections.abc import Callable, Sequence +from typing import TYPE_CHECKING, Any, Literal from gradio_client.documentation import document diff --git a/gradio/components/chatbot.py b/gradio/components/chatbot.py index 74caf6ae3d..ceaaa9f1fe 100644 --- a/gradio/components/chatbot.py +++ b/gradio/components/chatbot.py @@ -3,19 +3,14 @@ from __future__ import annotations import inspect +from collections.abc import Callable, Sequence from dataclasses import dataclass, field from pathlib import Path from typing import ( TYPE_CHECKING, Any, - Callable, - Dict, - List, Literal, Optional, - Sequence, - Tuple, - Type, TypedDict, Union, cast, @@ -66,13 +61,13 @@ class FileMessage(GradioModel): class ComponentMessage(GradioModel): component: str value: Any - constructor_args: Dict[str, Any] - props: Dict[str, Any] + constructor_args: dict[str, Any] + props: dict[str, Any] class ChatbotDataTuples(GradioRootModel): - root: List[ - Tuple[ + root: list[ + tuple[ Union[str, FileMessage, ComponentMessage, None], Union[str, FileMessage, ComponentMessage, None], ] @@ -97,10 +92,10 @@ class ChatMessage: class ChatbotDataMessages(GradioRootModel): - root: List[Message] + root: list[Message] -TupleFormat = List[List[Union[str, Tuple[str], Tuple[str, str], None]]] +TupleFormat = list[list[Union[str, tuple[str], tuple[str, str], None]]] if TYPE_CHECKING: from gradio.components import Timer @@ -485,7 +480,7 @@ def postprocess( an object of type ChatbotData """ data_model = cast( - Union[Type[ChatbotDataTuples], Type[ChatbotDataMessages]], self.data_model + Union[type[ChatbotDataTuples], type[ChatbotDataMessages]], self.data_model ) if value is None: return data_model(root=[]) diff --git a/gradio/components/checkbox.py b/gradio/components/checkbox.py index e4dc833ed3..ba3a5daf1c 100644 --- a/gradio/components/checkbox.py +++ b/gradio/components/checkbox.py @@ -2,7 +2,8 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Any, Callable, Sequence +from collections.abc import Callable, Sequence +from typing import TYPE_CHECKING, Any from gradio_client.documentation import document diff --git a/gradio/components/checkboxgroup.py b/gradio/components/checkboxgroup.py index 95cbdf2b68..c25839aa4d 100644 --- a/gradio/components/checkboxgroup.py +++ b/gradio/components/checkboxgroup.py @@ -2,7 +2,8 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Any, Callable, Literal, Sequence +from collections.abc import Callable, Sequence +from typing import TYPE_CHECKING, Any, Literal from gradio_client.documentation import document diff --git a/gradio/components/clear_button.py b/gradio/components/clear_button.py index f1617a74b9..f8ba9c4048 100644 --- a/gradio/components/clear_button.py +++ b/gradio/components/clear_button.py @@ -4,7 +4,8 @@ import copy import json -from typing import TYPE_CHECKING, Any, Literal, Sequence +from collections.abc import Sequence +from typing import TYPE_CHECKING, Any, Literal from gradio_client.documentation import document diff --git a/gradio/components/code.py b/gradio/components/code.py index 4393294613..80220f57f7 100644 --- a/gradio/components/code.py +++ b/gradio/components/code.py @@ -2,7 +2,8 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Any, Callable, Literal, Sequence +from collections.abc import Callable, Sequence +from typing import TYPE_CHECKING, Any, Literal from gradio_client.documentation import document @@ -159,10 +160,15 @@ def postprocess(self, value: str | None) -> None | str: Parameters: value: Expects a `str` of code. Returns: - Returns the code as a `str`. + Returns the code as a `str` stripped of leading and trailing whitespace. """ if value is None: return None + if isinstance(value, tuple): + raise ValueError( + "Code component does not support returning files as tuples anymore. " + "Please read the file contents and return as str instead." + ) return value.strip() def api_info(self) -> dict[str, Any]: diff --git a/gradio/components/color_picker.py b/gradio/components/color_picker.py index 163732ad31..0eee7c20c1 100644 --- a/gradio/components/color_picker.py +++ b/gradio/components/color_picker.py @@ -2,7 +2,8 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Any, Callable, Sequence +from collections.abc import Callable, Sequence +from typing import TYPE_CHECKING, Any from gradio_client.documentation import document diff --git a/gradio/components/dataframe.py b/gradio/components/dataframe.py index 4dd6f1a04b..e1380db7de 100644 --- a/gradio/components/dataframe.py +++ b/gradio/components/dataframe.py @@ -3,16 +3,12 @@ from __future__ import annotations import warnings +from collections.abc import Callable, Sequence from typing import ( TYPE_CHECKING, Any, - Callable, - Dict, - List, Literal, Optional, - Sequence, - Tuple, Union, ) @@ -46,9 +42,9 @@ def _import_polars(): class DataframeData(GradioModel): - headers: List[str] - data: Union[List[List[Any]], List[Tuple[Any, ...]]] - metadata: Optional[Dict[str, Optional[List[Any]]]] = None + headers: list[str] + data: Union[list[list[Any]], list[tuple[Any, ...]]] + metadata: Optional[dict[str, Optional[list[Any]]]] = None @document() diff --git a/gradio/components/dataset.py b/gradio/components/dataset.py index 468087aef2..9551c0052f 100644 --- a/gradio/components/dataset.py +++ b/gradio/components/dataset.py @@ -3,7 +3,8 @@ from __future__ import annotations import warnings -from typing import Any, Literal, Sequence +from collections.abc import Sequence +from typing import Any, Literal from gradio_client.documentation import document @@ -99,7 +100,7 @@ def __init__( self.samples: list[list] = [] for example in self.raw_samples: self.samples.append([]) - for component, ex in zip(self._components, example): + for component, ex in zip(self._components, example, strict=False): # If proxy_url is set, that means it is being loaded from an external Gradio app # which means that the example has already been processed. if self.proxy_url is None: diff --git a/gradio/components/download_button.py b/gradio/components/download_button.py index 15c5aec9ed..6c34718c09 100644 --- a/gradio/components/download_button.py +++ b/gradio/components/download_button.py @@ -3,8 +3,9 @@ from __future__ import annotations import tempfile +from collections.abc import Callable, Sequence from pathlib import Path -from typing import TYPE_CHECKING, Callable, Literal, Sequence +from typing import TYPE_CHECKING, Literal from gradio_client import handle_file from gradio_client.documentation import document diff --git a/gradio/components/dropdown.py b/gradio/components/dropdown.py index 5102e09374..1abe4ebbdf 100644 --- a/gradio/components/dropdown.py +++ b/gradio/components/dropdown.py @@ -3,7 +3,8 @@ from __future__ import annotations import warnings -from typing import TYPE_CHECKING, Any, Callable, Literal, Sequence +from collections.abc import Callable, Sequence +from typing import TYPE_CHECKING, Any, Literal from gradio_client.documentation import document diff --git a/gradio/components/duplicate_button.py b/gradio/components/duplicate_button.py index 90f01bc4c2..0094e57243 100644 --- a/gradio/components/duplicate_button.py +++ b/gradio/components/duplicate_button.py @@ -2,7 +2,8 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Literal, Sequence +from collections.abc import Sequence +from typing import TYPE_CHECKING, Literal from gradio_client.documentation import document diff --git a/gradio/components/file.py b/gradio/components/file.py index 4bc4503cd7..70dbcfd995 100644 --- a/gradio/components/file.py +++ b/gradio/components/file.py @@ -4,8 +4,9 @@ import tempfile import warnings +from collections.abc import Callable, Sequence from pathlib import Path -from typing import TYPE_CHECKING, Any, Callable, Literal, Sequence +from typing import TYPE_CHECKING, Any, Literal import gradio_client.utils as client_utils from gradio_client import handle_file diff --git a/gradio/components/file_explorer.py b/gradio/components/file_explorer.py index 7f9ca06313..8ad9e957f1 100644 --- a/gradio/components/file_explorer.py +++ b/gradio/components/file_explorer.py @@ -4,8 +4,9 @@ import fnmatch import os +from collections.abc import Callable, Sequence from pathlib import Path -from typing import TYPE_CHECKING, Any, Callable, List, Literal, Sequence +from typing import TYPE_CHECKING, Any, Literal from gradio_client.documentation import document @@ -20,7 +21,7 @@ class FileExplorerData(GradioRootModel): # The outer list is the list of files selected, and the inner list # is the path to the file as a list, split by the os.sep. - root: List[List[str]] + root: list[list[str]] @document() diff --git a/gradio/components/gallery.py b/gradio/components/gallery.py index 7a514ab988..4e2b00bcf2 100644 --- a/gradio/components/gallery.py +++ b/gradio/components/gallery.py @@ -2,17 +2,14 @@ from __future__ import annotations +from collections.abc import Callable, Sequence from concurrent.futures import ThreadPoolExecutor from pathlib import Path from typing import ( TYPE_CHECKING, Any, - Callable, - List, Literal, Optional, - Sequence, - Tuple, Union, ) from urllib.parse import urlparse @@ -32,7 +29,7 @@ from gradio.components import Timer GalleryImageType = Union[np.ndarray, PIL.Image.Image, Path, str] -CaptionedGalleryImageType = Tuple[GalleryImageType, str] +CaptionedGalleryImageType = tuple[GalleryImageType, str] class GalleryImage(GradioModel): @@ -41,7 +38,7 @@ class GalleryImage(GradioModel): class GalleryData(GradioRootModel): - root: List[GalleryImage] + root: list[GalleryImage] @document() @@ -78,7 +75,7 @@ def __init__( elem_classes: list[str] | str | None = None, render: bool = True, key: int | str | None = None, - columns: int | list[int] | Tuple[int, ...] | None = 2, + columns: int | list[int] | tuple[int, ...] | None = 2, rows: int | list[int] | None = None, height: int | float | str | None = None, allow_preview: bool = True, @@ -163,9 +160,9 @@ def __init__( def preprocess( self, payload: GalleryData | None ) -> ( - List[tuple[str, str | None]] - | List[tuple[PIL.Image.Image, str | None]] - | List[tuple[np.ndarray, str | None]] + list[tuple[str, str | None]] + | list[tuple[PIL.Image.Image, str | None]] + | list[tuple[np.ndarray, str | None]] | None ): """ diff --git a/gradio/components/highlighted_text.py b/gradio/components/highlighted_text.py index 58a2486eec..17200579d6 100644 --- a/gradio/components/highlighted_text.py +++ b/gradio/components/highlighted_text.py @@ -2,7 +2,8 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Any, Callable, List, Sequence, Union +from collections.abc import Callable, Sequence +from typing import TYPE_CHECKING, Any, Union from gradio_client.documentation import document @@ -20,7 +21,7 @@ class HighlightedToken(GradioModel): class HighlightedTextData(GradioRootModel): - root: List[HighlightedToken] + root: list[HighlightedToken] @document() diff --git a/gradio/components/html.py b/gradio/components/html.py index e3fd3c17cb..eac6a277b4 100644 --- a/gradio/components/html.py +++ b/gradio/components/html.py @@ -2,7 +2,8 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Any, Callable, Sequence +from collections.abc import Callable, Sequence +from typing import TYPE_CHECKING, Any from gradio_client.documentation import document diff --git a/gradio/components/image.py b/gradio/components/image.py index 67ff6f9466..b4f7713718 100644 --- a/gradio/components/image.py +++ b/gradio/components/image.py @@ -3,8 +3,9 @@ from __future__ import annotations import warnings +from collections.abc import Callable, Sequence from pathlib import Path -from typing import TYPE_CHECKING, Any, Callable, Literal, Sequence, cast +from typing import TYPE_CHECKING, Any, Literal, cast import numpy as np import PIL.Image diff --git a/gradio/components/image_editor.py b/gradio/components/image_editor.py index b59798760b..4fa0bf8272 100644 --- a/gradio/components/image_editor.py +++ b/gradio/components/image_editor.py @@ -4,17 +4,14 @@ import dataclasses import warnings +from collections.abc import Iterable, Sequence from io import BytesIO from pathlib import Path from typing import ( TYPE_CHECKING, Any, - Iterable, - List, Literal, Optional, - Sequence, - Tuple, Union, cast, ) @@ -50,14 +47,14 @@ class EditorExampleValue(TypedDict): class EditorData(GradioModel): background: Optional[FileData] = None - layers: List[FileData] = [] + layers: list[FileData] = [] composite: Optional[FileData] = None id: Optional[str] = None class EditorDataBlobs(GradioModel): background: Optional[bytes] - layers: List[Union[bytes, None]] + layers: list[Union[bytes, None]] composite: Optional[bytes] @@ -70,7 +67,7 @@ class BlobData(TypedDict): class AcceptBlobs(GradioModel): data: BlobData - files: List[Tuple[str, bytes]] + files: list[tuple[str, bytes]] @document() diff --git a/gradio/components/json_component.py b/gradio/components/json_component.py index 5f57f89d71..7a8754af7e 100644 --- a/gradio/components/json_component.py +++ b/gradio/components/json_component.py @@ -3,11 +3,10 @@ from __future__ import annotations import json +from collections.abc import Callable, Sequence from typing import ( TYPE_CHECKING, Any, - Callable, - Sequence, ) import orjson diff --git a/gradio/components/label.py b/gradio/components/label.py index fd623111d8..21fdbe68e4 100644 --- a/gradio/components/label.py +++ b/gradio/components/label.py @@ -4,8 +4,9 @@ import json import operator +from collections.abc import Callable, Sequence from pathlib import Path -from typing import TYPE_CHECKING, Any, Callable, List, Optional, Sequence, Union +from typing import TYPE_CHECKING, Any, Optional, Union from gradio_client.documentation import document @@ -24,7 +25,7 @@ class LabelConfidence(GradioModel): class LabelData(GradioModel): label: Optional[Union[str, int, float]] = None - confidences: Optional[List[LabelConfidence]] = None + confidences: Optional[list[LabelConfidence]] = None @document() diff --git a/gradio/components/line_plot.py b/gradio/components/line_plot.py index ebddcc278c..c80b885989 100644 --- a/gradio/components/line_plot.py +++ b/gradio/components/line_plot.py @@ -3,7 +3,8 @@ from __future__ import annotations import warnings -from typing import TYPE_CHECKING, Any, Callable, Literal, Sequence +from collections.abc import Callable, Sequence +from typing import TYPE_CHECKING, Any, Literal from gradio_client.documentation import document diff --git a/gradio/components/login_button.py b/gradio/components/login_button.py index 35bba307fc..1a062a1ce4 100644 --- a/gradio/components/login_button.py +++ b/gradio/components/login_button.py @@ -5,7 +5,8 @@ import json import time import warnings -from typing import TYPE_CHECKING, Literal, Sequence +from collections.abc import Sequence +from typing import TYPE_CHECKING, Literal from gradio_client.documentation import document diff --git a/gradio/components/logout_button.py b/gradio/components/logout_button.py index 9cee4098ea..c064e7994e 100644 --- a/gradio/components/logout_button.py +++ b/gradio/components/logout_button.py @@ -3,7 +3,8 @@ from __future__ import annotations import warnings -from typing import TYPE_CHECKING, Literal, Sequence +from collections.abc import Sequence +from typing import TYPE_CHECKING, Literal from gradio_client.documentation import document diff --git a/gradio/components/markdown.py b/gradio/components/markdown.py index 265d65d68b..98e0489796 100644 --- a/gradio/components/markdown.py +++ b/gradio/components/markdown.py @@ -3,7 +3,8 @@ from __future__ import annotations import inspect -from typing import TYPE_CHECKING, Any, Callable, Sequence +from collections.abc import Callable, Sequence +from typing import TYPE_CHECKING, Any from gradio_client.documentation import document diff --git a/gradio/components/model3d.py b/gradio/components/model3d.py index f421b8a9ec..a4a6744c66 100644 --- a/gradio/components/model3d.py +++ b/gradio/components/model3d.py @@ -2,8 +2,9 @@ from __future__ import annotations +from collections.abc import Callable, Sequence from pathlib import Path -from typing import TYPE_CHECKING, Callable, Literal, Sequence +from typing import TYPE_CHECKING, Literal from gradio_client import handle_file from gradio_client.documentation import document diff --git a/gradio/components/multimodal_textbox.py b/gradio/components/multimodal_textbox.py index d91051a4c4..aee8ccfcf0 100644 --- a/gradio/components/multimodal_textbox.py +++ b/gradio/components/multimodal_textbox.py @@ -2,8 +2,9 @@ from __future__ import annotations +from collections.abc import Callable, Sequence from pathlib import Path -from typing import TYPE_CHECKING, Any, Callable, List, Literal, Sequence, TypedDict +from typing import TYPE_CHECKING, Any, Literal, TypedDict import gradio_client.utils as client_utils from gradio_client.documentation import document @@ -20,12 +21,12 @@ class MultimodalData(GradioModel): text: str - files: List[FileData] = Field(default_factory=list) + files: list[FileData] = Field(default_factory=list) class MultimodalPostprocess(TypedDict): text: str - files: List[FileData] + files: list[FileData] class MultimodalValue(TypedDict): diff --git a/gradio/components/native_plot.py b/gradio/components/native_plot.py index 8414fd68dc..c840660f7a 100644 --- a/gradio/components/native_plot.py +++ b/gradio/components/native_plot.py @@ -2,15 +2,11 @@ import json import warnings +from collections.abc import Callable, Sequence, Set from typing import ( TYPE_CHECKING, - AbstractSet, Any, - Callable, - Dict, - List, Literal, - Sequence, ) import pandas as pd @@ -25,9 +21,9 @@ class PlotData(GradioModel): - columns: List[str] - data: List[List[Any]] - datatypes: Dict[str, Literal["quantitative", "nominal", "temporal"]] + columns: list[str] + data: list[list[Any]] + datatypes: dict[str, Literal["quantitative", "nominal", "temporal"]] mark: str @@ -68,7 +64,7 @@ def __init__( scale: int | None = None, min_width: int = 160, every: Timer | float | None = None, - inputs: Component | Sequence[Component] | AbstractSet[Component] | None = None, + inputs: Component | Sequence[Component] | Set[Component] | None = None, visible: bool = True, elem_id: str | None = None, elem_classes: list[str] | str | None = None, diff --git a/gradio/components/number.py b/gradio/components/number.py index 05b2e21f2d..b79e201002 100644 --- a/gradio/components/number.py +++ b/gradio/components/number.py @@ -2,7 +2,8 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Any, Callable, Sequence +from collections.abc import Callable, Sequence +from typing import TYPE_CHECKING, Any from gradio_client.documentation import document diff --git a/gradio/components/paramviewer.py b/gradio/components/paramviewer.py index 1d20d81c2c..4b244694fe 100644 --- a/gradio/components/paramviewer.py +++ b/gradio/components/paramviewer.py @@ -1,6 +1,7 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Literal, Mapping, Sequence, TypedDict +from collections.abc import Mapping, Sequence +from typing import TYPE_CHECKING, Literal, TypedDict from gradio_client.documentation import document diff --git a/gradio/components/plot.py b/gradio/components/plot.py index 166ccac380..02c21b1531 100644 --- a/gradio/components/plot.py +++ b/gradio/components/plot.py @@ -3,8 +3,9 @@ from __future__ import annotations import json +from collections.abc import Sequence from types import ModuleType -from typing import TYPE_CHECKING, Any, Literal, Sequence +from typing import TYPE_CHECKING, Any, Literal from gradio_client.documentation import document diff --git a/gradio/components/radio.py b/gradio/components/radio.py index 414118eae4..16021eb2ed 100644 --- a/gradio/components/radio.py +++ b/gradio/components/radio.py @@ -2,7 +2,8 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Any, Callable, Literal, Sequence +from collections.abc import Callable, Sequence +from typing import TYPE_CHECKING, Any, Literal from gradio_client.documentation import document diff --git a/gradio/components/scatter_plot.py b/gradio/components/scatter_plot.py index 5351971a26..0557554864 100644 --- a/gradio/components/scatter_plot.py +++ b/gradio/components/scatter_plot.py @@ -3,7 +3,8 @@ from __future__ import annotations import warnings -from typing import TYPE_CHECKING, Any, Callable, Literal, Sequence +from collections.abc import Callable, Sequence +from typing import TYPE_CHECKING, Any, Literal from gradio_client.documentation import document diff --git a/gradio/components/slider.py b/gradio/components/slider.py index 384d3f3239..076df2faf1 100644 --- a/gradio/components/slider.py +++ b/gradio/components/slider.py @@ -4,7 +4,8 @@ import math import random -from typing import TYPE_CHECKING, Any, Callable, Sequence +from collections.abc import Callable, Sequence +from typing import TYPE_CHECKING, Any from gradio_client.documentation import document diff --git a/gradio/components/state.py b/gradio/components/state.py index 305ce37ec7..e77f3c81b6 100644 --- a/gradio/components/state.py +++ b/gradio/components/state.py @@ -3,8 +3,9 @@ from __future__ import annotations import math +from collections.abc import Callable from copy import deepcopy -from typing import Any, Callable +from typing import Any from gradio_client.documentation import document diff --git a/gradio/components/textbox.py b/gradio/components/textbox.py index 32202a9b04..7f38b3639e 100644 --- a/gradio/components/textbox.py +++ b/gradio/components/textbox.py @@ -2,7 +2,8 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Any, Callable, Literal, Sequence +from collections.abc import Callable, Sequence +from typing import TYPE_CHECKING, Any, Literal from gradio_client.documentation import document diff --git a/gradio/components/upload_button.py b/gradio/components/upload_button.py index a75cf757e7..d377cd73a8 100644 --- a/gradio/components/upload_button.py +++ b/gradio/components/upload_button.py @@ -4,8 +4,9 @@ import tempfile import warnings +from collections.abc import Callable, Sequence from pathlib import Path -from typing import TYPE_CHECKING, Any, Callable, Literal, Sequence +from typing import TYPE_CHECKING, Any, Literal import gradio_client.utils as client_utils from gradio_client import handle_file diff --git a/gradio/components/video.py b/gradio/components/video.py index 9f5bb4f862..8464f3a572 100644 --- a/gradio/components/video.py +++ b/gradio/components/video.py @@ -7,8 +7,9 @@ import subprocess import tempfile import warnings +from collections.abc import Callable, Sequence from pathlib import Path -from typing import TYPE_CHECKING, Any, Callable, Literal, Optional, Sequence +from typing import TYPE_CHECKING, Any, Literal, Optional from gradio_client import handle_file from gradio_client import utils as client_utils @@ -378,9 +379,10 @@ def _format_subtitle(self, subtitle: str | Path | None) -> FileData | None: def srt_to_vtt(srt_file_path, vtt_file_path): """Convert an SRT subtitle file to a VTT subtitle file""" - with open(srt_file_path, encoding="utf-8") as srt_file, open( - vtt_file_path, "w", encoding="utf-8" - ) as vtt_file: + with ( + open(srt_file_path, encoding="utf-8") as srt_file, + open(vtt_file_path, "w", encoding="utf-8") as vtt_file, + ): vtt_file.write("WEBVTT\n\n") for subtitle_block in srt_file.read().strip().split("\n\n"): subtitle_lines = subtitle_block.split("\n") diff --git a/gradio/data_classes.py b/gradio/data_classes.py index ba02f4d6d1..0311ea8f85 100644 --- a/gradio/data_classes.py +++ b/gradio/data_classes.py @@ -7,15 +7,13 @@ import secrets import shutil from abc import ABC, abstractmethod +from collections.abc import Iterator from enum import Enum, auto from typing import ( Any, - Iterator, - List, Literal, NewType, Optional, - Tuple, TypedDict, Union, ) @@ -42,7 +40,7 @@ class CancelBody(BaseModel): class SimplePredictBody(BaseModel): - data: List[Any] + data: list[Any] session_hash: Optional[str] = None @@ -51,7 +49,7 @@ class PredictBody(BaseModel): session_hash: Optional[str] = None event_id: Optional[str] = None - data: List[Any] + data: list[Any] event_data: Optional[Any] = None fn_index: Optional[int] = None trigger_id: Optional[int] = None @@ -96,7 +94,7 @@ class ComponentServerJSONBody(BaseModel): class DataWithFiles(BaseModel): data: Any - files: List[Tuple[str, bytes]] + files: list[tuple[str, bytes]] class ComponentServerBlobBody(BaseModel): @@ -268,7 +266,7 @@ def is_file_data(cls, obj: Any) -> bool: class ListFiles(GradioRootModel): - root: List[FileData] + root: list[FileData] def __getitem__(self, index): return self.root[index] diff --git a/gradio/events.py b/gradio/events.py index 243f466b65..cf35ff61ab 100644 --- a/gradio/events.py +++ b/gradio/events.py @@ -4,16 +4,12 @@ from __future__ import annotations import dataclasses +from collections.abc import Callable, Sequence, Set from functools import partial, wraps from typing import ( TYPE_CHECKING, - AbstractSet, Any, - Callable, - Dict, - List, Literal, - Sequence, Union, cast, ) @@ -322,7 +318,7 @@ class EventListenerMethod: int, bool, bool, - Union[Dict[str, Any], List[Dict[str, Any]], None], + Union[dict[str, Any], list[dict[str, Any]], None], Union[float, None], Union[Literal["once", "multiple", "always_last"], None], Union[str, None], @@ -406,12 +402,12 @@ def event_trigger( inputs: Component | BlockContext | Sequence[Component | BlockContext] - | AbstractSet[Component | BlockContext] + | Set[Component | BlockContext] | None = None, outputs: Component | BlockContext | Sequence[Component | BlockContext] - | AbstractSet[Component | BlockContext] + | Set[Component | BlockContext] | None = None, api_name: str | None | Literal[False] = None, scroll_to_output: bool = False, @@ -545,12 +541,12 @@ def on( inputs: Component | BlockContext | Sequence[Component | BlockContext] - | AbstractSet[Component | BlockContext] + | Set[Component | BlockContext] | None = None, outputs: Component | BlockContext | Sequence[Component | BlockContext] - | AbstractSet[Component | BlockContext] + | Set[Component | BlockContext] | None = None, *, api_name: str | None | Literal[False] = None, diff --git a/gradio/external.py b/gradio/external.py index 7f2e4c32e2..3cca34d905 100644 --- a/gradio/external.py +++ b/gradio/external.py @@ -8,8 +8,9 @@ import re import tempfile import warnings +from collections.abc import Callable from pathlib import Path -from typing import TYPE_CHECKING, Callable, Literal +from typing import TYPE_CHECKING, Literal import httpx import huggingface_hub diff --git a/gradio/external_utils.py b/gradio/external_utils.py index ab982c0f92..7126489b49 100644 --- a/gradio/external_utils.py +++ b/gradio/external_utils.py @@ -204,6 +204,7 @@ def chatbot_postprocess(response): zip( response["conversation"]["past_user_inputs"], response["conversation"]["generated_responses"], + strict=False, ) ) return chatbot_history, response diff --git a/gradio/flagging.py b/gradio/flagging.py index f2d03e6cd5..490784377b 100644 --- a/gradio/flagging.py +++ b/gradio/flagging.py @@ -5,8 +5,9 @@ import os import time from abc import ABC, abstractmethod +from collections.abc import Sequence from pathlib import Path -from typing import TYPE_CHECKING, Any, Sequence +from typing import TYPE_CHECKING, Any from gradio_client import utils as client_utils from gradio_client.documentation import document @@ -87,7 +88,7 @@ def flag( log_filepath = Path(flagging_dir) / "log.csv" csv_data = [] - for component, sample in zip(self.components, flag_data): + for component, sample in zip(self.components, flag_data, strict=False): save_dir = Path( flagging_dir ) / client_utils.strip_invalid_filename_characters(component.label or "") @@ -153,7 +154,9 @@ def flag( ] csv_data = [] - for idx, (component, sample) in enumerate(zip(self.components, flag_data)): + for idx, (component, sample) in enumerate( + zip(self.components, flag_data, strict=False) + ): save_dir = Path( flagging_dir ) / client_utils.strip_invalid_filename_characters( diff --git a/gradio/helpers.py b/gradio/helpers.py index e70ad9e3f0..f4a5f45463 100644 --- a/gradio/helpers.py +++ b/gradio/helpers.py @@ -13,9 +13,10 @@ import subprocess import tempfile import warnings +from collections.abc import Callable, Iterable, Sequence from functools import partial from pathlib import Path -from typing import TYPE_CHECKING, Any, Callable, Iterable, Literal, Optional, Sequence +from typing import TYPE_CHECKING, Any, Literal, Optional import numpy as np import PIL @@ -227,10 +228,14 @@ def __init__( pass # If there are more example components than inputs, ignore. This can sometimes be intentional (e.g. loading from a log file where outputs and timestamps are also logged) inputs_with_examples = [ - inp for (inp, keep) in zip(inputs, input_has_examples) if keep + inp for (inp, keep) in zip(inputs, input_has_examples, strict=False) if keep ] non_none_examples = [ - [ex for (ex, keep) in zip(example, input_has_examples) if keep] + [ + ex + for (ex, keep) in zip(example, input_has_examples, strict=False) + if keep + ] for example in examples ] if example_labels is not None and len(example_labels) != len(examples): @@ -286,7 +291,7 @@ def _get_processed_example(self, example): return self.non_none_processed_examples[example] with utils.set_directory(self.working_directory): sub = [] - for component, sample in zip(self.inputs, example): + for component, sample in zip(self.inputs, example, strict=False): prediction_value = component.postprocess(sample) if isinstance(prediction_value, (GradioRootModel, GradioModel)): prediction_value = prediction_value.model_dump() @@ -296,7 +301,9 @@ def _get_processed_example(self, example): postprocess=True, ) sub.append(prediction_value) - return [ex for (ex, keep) in zip(sub, self.input_has_examples) if keep] + return [ + ex for (ex, keep) in zip(sub, self.input_has_examples, strict=False) if keep + ] def create(self) -> None: """Caches the examples if self.cache_examples is True and creates the Dataset @@ -564,7 +571,7 @@ def load_from_cache(self, example_id: int) -> list[Any]: output = [] if self.outputs is None: raise ValueError("self.outputs is missing") - for component, value in zip(self.outputs, example): + for component, value in zip(self.outputs, example, strict=False): value_to_use = value try: value_as_dict = ast.literal_eval(value) @@ -908,12 +915,8 @@ def special_args( if inputs is not None: inputs.insert(i, request) elif type_hint in ( - # Note: "OAuthProfile | None" is equals to Optional[OAuthProfile] in Python - # => it is automatically handled as well by the above condition - # (adding explicit "OAuthProfile | None" would break in Python3.9) - # (same for "OAuthToken") - Optional[oauth.OAuthProfile], - Optional[oauth.OAuthToken], + oauth.OAuthProfile | None, + oauth.OAuthToken | None, oauth.OAuthProfile, oauth.OAuthToken, ): diff --git a/gradio/interface.py b/gradio/interface.py index ce7d98e8dd..382115c110 100644 --- a/gradio/interface.py +++ b/gradio/interface.py @@ -9,7 +9,8 @@ import os import warnings import weakref -from typing import TYPE_CHECKING, Any, Callable, Literal, Sequence +from collections.abc import Callable, Sequence +from typing import TYPE_CHECKING, Any, Literal from gradio_client.documentation import document @@ -289,7 +290,10 @@ def __init__( if len(self.input_components) == len(self.output_components): same_components = [ - i is o for i, o in zip(self.input_components, self.output_components) + i is o + for i, o in zip( + self.input_components, self.output_components, strict=False + ) ] if all(same_components): self.interface_type = InterfaceTypes.UNIFIED @@ -445,7 +449,9 @@ def __init__( param_names.remove(param_name) except (TypeError, ValueError): param_names = utils.default_input_labels() - for component, param_name in zip(self.input_components, param_names): + for component, param_name in zip( + self.input_components, param_names, strict=False + ): if not isinstance(component, Component): raise TypeError( f"Input component must be a Component, not {type(component)}" @@ -834,7 +840,9 @@ def attach_flagging_events( else: flag_components = self.input_components + self.output_components - for flag_btn, (label, value) in zip(flag_btns, self.flagging_options): + for flag_btn, (label, value) in zip( + flag_btns, self.flagging_options, strict=False + ): if not isinstance(value, str): raise TypeError( f"Flagging option value must be a string, not {value!r}" @@ -951,7 +959,7 @@ def __init__( f"

{title}

" ) with Tabs(): - for interface, tab_name in zip(interface_list, tab_names): + for interface, tab_name in zip(interface_list, tab_names, strict=False): with Tab(label=tab_name): interface.render() diff --git a/gradio/pipelines_utils.py b/gradio/pipelines_utils.py index b1908dc291..a6ffa6e689 100644 --- a/gradio/pipelines_utils.py +++ b/gradio/pipelines_utils.py @@ -3,7 +3,7 @@ These are used by load_from_pipeline method in pipelines.py. """ -from typing import Any, Dict, Optional +from typing import Any, Optional import numpy as np from PIL import Image @@ -11,7 +11,7 @@ from gradio import components -def handle_transformers_pipeline(pipeline: Any) -> Optional[Dict[str, Any]]: +def handle_transformers_pipeline(pipeline: Any) -> Optional[dict[str, Any]]: try: import transformers except ImportError as ie: @@ -196,7 +196,7 @@ def is_transformers_pipeline_type(pipeline, class_name: str): raise ValueError(f"Unsupported transformers pipeline type: {type(pipeline)}") -def handle_diffusers_pipeline(pipeline: Any) -> Optional[Dict[str, Any]]: +def handle_diffusers_pipeline(pipeline: Any) -> Optional[dict[str, Any]]: try: import diffusers except ImportError as ie: @@ -488,7 +488,7 @@ def is_diffusers_pipeline_type(pipeline, class_name: str): raise ValueError(f"Unsupported diffusers pipeline type: {type(pipeline)}") -def handle_transformers_js_pipeline(pipeline: Any) -> Dict[str, Any]: +def handle_transformers_js_pipeline(pipeline: Any) -> dict[str, Any]: try: from transformers_js_py import as_url, read_audio # type: ignore except ImportError as ie: @@ -605,7 +605,9 @@ def handle_transformers_js_pipeline(pipeline: Any) -> Dict[str, Any]: text, [c.strip() for c in classnames.split(",")], ), - "postprocess": lambda result: dict(zip(result["labels"], result["scores"])), + "postprocess": lambda result: dict( + zip(result["labels"], result["scores"], strict=False) + ), } if pipeline.task == "feature-extraction": return { diff --git a/gradio/processing_utils.py b/gradio/processing_utils.py index 364f8b6246..b003ec57d8 100644 --- a/gradio/processing_utils.py +++ b/gradio/processing_utils.py @@ -309,9 +309,10 @@ def save_url_to_cache(url: str, cache_dir: str) -> str: full_temp_file_path = str(abspath(temp_dir / name)) if not Path(full_temp_file_path).exists(): - with sync_client.stream("GET", url, follow_redirects=True) as response, open( - full_temp_file_path, "wb" - ) as f: + with ( + sync_client.stream("GET", url, follow_redirects=True) as response, + open(full_temp_file_path, "wb") as f, + ): for redirect in response.history: check_public_url(str(redirect.url)) diff --git a/gradio/queueing.py b/gradio/queueing.py index 8fa5dea71b..1eba6f31ed 100644 --- a/gradio/queueing.py +++ b/gradio/queueing.py @@ -9,10 +9,9 @@ import uuid from collections import defaultdict from queue import Queue as ThreadQueue -from typing import TYPE_CHECKING, cast +from typing import TYPE_CHECKING, Literal, cast import fastapi -from typing_extensions import Literal from gradio import route_utils, routes from gradio.data_classes import ( @@ -540,7 +539,7 @@ async def wait_for_batch( events: list[Event], timeouts: list[float] ) -> tuple[list[Event], list[Event]]: tasks = [] - for event, timeout in zip(events, timeouts): + for event, timeout in zip(events, timeouts, strict=False): tasks.append( asyncio.create_task(Queue.wait_for_event_or_timeout(event, timeout)) ) @@ -551,7 +550,7 @@ async def wait_for_batch( done = [d.result() for d in done] awake_events = [] closed_events = [] - for result, event in zip(done, events): + for result, event in zip(done, events, strict=False): if result == "signal": awake_events.append(event) else: @@ -592,7 +591,10 @@ async def process_events( if batch: body.data = list( - zip(*[event.data.data for event in events if event.data]) + zip( + *[event.data.data for event in events if event.data], + strict=False, + ) ) body.request = events[0].request body.batched = True @@ -687,7 +689,12 @@ async def process_events( if batch: body.data = list( zip( - *[event.data.data for event in events if event.data] + *[ + event.data.data + for event in events + if event.data + ], + strict=False, ) ) response = await route_utils.call_process_api( @@ -723,7 +730,9 @@ async def process_events( output = copy.deepcopy(response) for e, event in enumerate(awake_events): if batch and "data" in output: - output["data"] = list(zip(*response.get("data")))[e] + output["data"] = list(zip(*response.get("data"), strict=False))[ + e + ] success = response is not None self.send_message( event, diff --git a/gradio/renderable.py b/gradio/renderable.py index dfa75851ec..5e3b004499 100644 --- a/gradio/renderable.py +++ b/gradio/renderable.py @@ -1,6 +1,7 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Callable, List, Literal, Sequence, Union, cast +from collections.abc import Callable, Sequence +from typing import TYPE_CHECKING, Literal, Union, cast from gradio_client.documentation import document @@ -119,7 +120,7 @@ def show_split(text): btn = gr.Button("Clear") btn.click(lambda: gr.Textbox(value=""), None, text) """ - new_triggers = cast(Union[List[EventListener], EventListener, None], triggers) + new_triggers = cast(Union[list[EventListener], EventListener, None], triggers) if Context.root_block is None: raise ValueError("Reactive render must be inside a Blocks context.") diff --git a/gradio/route_utils.py b/gradio/route_utils.py index 703056e309..09ff1cf353 100644 --- a/gradio/route_utils.py +++ b/gradio/route_utils.py @@ -8,24 +8,19 @@ import os import re import shutil -import sys import threading import uuid from collections import deque -from contextlib import AsyncExitStack, asynccontextmanager +from collections.abc import AsyncGenerator, Callable +from contextlib import AbstractAsyncContextManager, AsyncExitStack, asynccontextmanager from dataclasses import dataclass as python_dataclass from datetime import datetime from pathlib import Path from tempfile import NamedTemporaryFile, _TemporaryFileWrapper from typing import ( TYPE_CHECKING, - AsyncContextManager, - AsyncGenerator, BinaryIO, - Callable, - List, Optional, - Tuple, Union, ) from urllib.parse import urlparse @@ -485,7 +480,7 @@ def __init__( self.stream = stream self.max_files = max_files self.max_fields = max_fields - self.items: List[Tuple[str, Union[str, UploadFile]]] = [] + self.items: list[tuple[str, Union[str, UploadFile]]] = [] self.upload_id = upload_id self.upload_progress = upload_progress self._current_files = 0 @@ -495,9 +490,9 @@ def __init__( self._current_partial_header_value: bytes = b"" self._current_part = MultipartPart() self._charset = "" - self._file_parts_to_write: List[Tuple[MultipartPart, bytes]] = [] - self._file_parts_to_finish: List[MultipartPart] = [] - self._files_to_close_on_error: List[_TemporaryFileWrapper] = [] + self._file_parts_to_write: list[tuple[MultipartPart, bytes]] = [] + self._file_parts_to_finish: list[MultipartPart] = [] + self._files_to_close_on_error: list[_TemporaryFileWrapper] = [] def on_part_begin(self) -> None: self._current_part = MultipartPart() @@ -646,7 +641,7 @@ async def parse(self) -> FormData: def move_uploaded_files_to_cache(files: list[str], destinations: list[str]) -> None: - for file, dest in zip(files, destinations): + for file, dest in zip(files, destinations, strict=False): shutil.move(file, dest) @@ -832,20 +827,15 @@ async def _delete_state(app: App): @asynccontextmanager async def _delete_state_handler(app: App): """When the server launches, regularly delete expired state.""" - # The stop event needs to get the current event loop for python 3.8 - # but the loop parameter is deprecated for 3.8+ - if sys.version_info < (3, 10): - loop = asyncio.get_running_loop() - app.stop_event = asyncio.Event(loop=loop) asyncio.create_task(_delete_state(app)) yield def create_lifespan_handler( - user_lifespan: Callable[[App], AsyncContextManager] | None, + user_lifespan: Callable[[App], AbstractAsyncContextManager] | None, frequency: int | None = 1, age: int | None = 1, -) -> Callable[[App], AsyncContextManager]: +) -> Callable[[App], AbstractAsyncContextManager]: """Return a context manager that applies _lifespan_handler and user_lifespan if it exists.""" @asynccontextmanager diff --git a/gradio/routes.py b/gradio/routes.py index 959cab7f7d..4163bb2ca2 100644 --- a/gradio/routes.py +++ b/gradio/routes.py @@ -5,34 +5,25 @@ import asyncio import contextlib -import math -import sys -import warnings - -if sys.version_info >= (3, 9): - from importlib.resources import files -else: - from importlib_resources import files import hashlib import inspect import json +import math import mimetypes import os import secrets +import sys import time import traceback +import warnings +from collections.abc import AsyncIterator, Callable from pathlib import Path from queue import Empty as EmptyQueue from typing import ( TYPE_CHECKING, Any, - AsyncIterator, - Callable, - Dict, - List, Literal, Optional, - Type, Union, cast, ) @@ -54,6 +45,7 @@ from gradio_client import utils as client_utils from gradio_client.documentation import document from gradio_client.utils import ServerMessage +from importlib_resources import files from jinja2.exceptions import TemplateNotFound from multipart.multipart import parse_options_header from starlette.background import BackgroundTask @@ -244,7 +236,7 @@ def _cancel_asyncio_tasks(self): @staticmethod def create_app( blocks: gradio.Blocks, - app_kwargs: Dict[str, Any] | None = None, + app_kwargs: dict[str, Any] | None = None, auth_dependency: Callable[[fastapi.Request], str | None] | None = None, strict_cors: bool = True, ) -> App: @@ -1299,7 +1291,7 @@ def routes_safe_join(directory: DeveloperPath, path: UserProvidedPath) -> str: return str(fullpath) -def get_types(cls_set: List[Type]): +def get_types(cls_set: list[type]): docset = [] types = [] for cls in cls_set: diff --git a/gradio/server_messages.py b/gradio/server_messages.py index 4222b84e23..0c746bef88 100644 --- a/gradio/server_messages.py +++ b/gradio/server_messages.py @@ -1,4 +1,4 @@ -from typing import List, Literal, Optional, Union +from typing import Literal, Optional, Union from gradio_client.utils import ServerMessage from pydantic import BaseModel @@ -19,7 +19,7 @@ class ProgressUnit(BaseModel): class ProgressMessage(BaseMessage): msg: Literal[ServerMessage.progress] = ServerMessage.progress # type: ignore - progress_data: List[ProgressUnit] = [] + progress_data: list[ProgressUnit] = [] class LogMessage(BaseMessage): diff --git a/gradio/state_holder.py b/gradio/state_holder.py index 13783fcc7a..133865dabb 100644 --- a/gradio/state_holder.py +++ b/gradio/state_holder.py @@ -4,8 +4,9 @@ import os import threading from collections import OrderedDict +from collections.abc import Iterator from copy import copy, deepcopy -from typing import TYPE_CHECKING, Any, Iterator +from typing import TYPE_CHECKING, Any if TYPE_CHECKING: from gradio.blocks import Blocks diff --git a/gradio/strings.py b/gradio/strings.py index f2e0a08409..4e2dad2288 100644 --- a/gradio/strings.py +++ b/gradio/strings.py @@ -1,6 +1,5 @@ import os import threading -from typing import Dict import httpx @@ -28,7 +27,7 @@ } -def get_updated_messaging(en: Dict): +def get_updated_messaging(en: dict): try: updated_messaging = httpx.get(MESSAGING_API_ENDPOINT, timeout=3).json() en.update(updated_messaging) diff --git a/gradio/templates.py b/gradio/templates.py index 2c8f9536c0..cb8e625522 100644 --- a/gradio/templates.py +++ b/gradio/templates.py @@ -1,7 +1,8 @@ from __future__ import annotations +from collections.abc import Callable, Iterable, Sequence from pathlib import Path -from typing import TYPE_CHECKING, Any, Callable, Iterable, Literal, Sequence +from typing import TYPE_CHECKING, Any, Literal import numpy as np import PIL.Image diff --git a/gradio/themes/base.py b/gradio/themes/base.py index 93c53f52a7..bee7dea537 100644 --- a/gradio/themes/base.py +++ b/gradio/themes/base.py @@ -5,8 +5,8 @@ import tempfile import textwrap import warnings +from collections.abc import Iterable from pathlib import Path -from typing import Iterable import huggingface_hub import semantic_version as semver diff --git a/gradio/themes/builder_app.py b/gradio/themes/builder_app.py index 8d6dd5c2c6..4c2c48371f 100644 --- a/gradio/themes/builder_app.py +++ b/gradio/themes/builder_app.py @@ -1,6 +1,6 @@ import inspect import time -from typing import Iterable +from collections.abc import Iterable from gradio_client.documentation import document_fn @@ -598,6 +598,7 @@ def generate_theme_code( gr.themes.Size, ], core_variables, + strict=False, ): if base_value.name != final_value: core_diffs[value_name] = final_value @@ -649,7 +650,9 @@ def generate_theme_code( if len(base_font_set) != len(theme_font_set) or any( base_font.name != theme_font[0] or isinstance(base_font, gr.themes.GoogleFont) != theme_font[1] - for base_font, theme_font in zip(base_font_set, theme_font_set) + for base_font, theme_font in zip( + base_font_set, theme_font_set, strict=False + ) ): font_diffs[font_set_name] = [ f"gr.themes.GoogleFont('{font_name}')" @@ -770,19 +773,19 @@ def render_variables(history, base_theme, *args): final_main_fonts = [] font_weights = set() - for attr, val in zip(flat_variables, remaining_args): + for attr, val in zip(flat_variables, remaining_args, strict=False): if "weight" in attr: font_weights.add(val) font_weights = sorted(font_weights) - for main_font, is_google in zip(main_fonts, main_is_google): + for main_font, is_google in zip(main_fonts, main_is_google, strict=False): if not main_font: continue if is_google: main_font = gr.themes.GoogleFont(main_font, weights=font_weights) final_main_fonts.append(main_font) final_mono_fonts = [] - for mono_font, is_google in zip(mono_fonts, mono_is_google): + for mono_font, is_google in zip(mono_fonts, mono_is_google, strict=False): if not mono_font: continue if is_google: @@ -800,7 +803,7 @@ def render_variables(history, base_theme, *args): font_mono=final_mono_fonts, ) - theme.set(**dict(zip(flat_variables, remaining_args))) + theme.set(**dict(zip(flat_variables, remaining_args, strict=False))) new_step = (base_theme, args) if len(history) == 0 or str(history[-1]) != str(new_step): history.append(new_step) @@ -820,8 +823,8 @@ def render_variables(history, base_theme, *args): spacing_size, radius_size, ), - list(zip(main_fonts, main_is_google)), - list(zip(mono_fonts, mono_is_google)), + list(zip(main_fonts, main_is_google, strict=False)), + list(zip(mono_fonts, mono_is_google, strict=False)), ), theme, ) diff --git a/gradio/themes/default.py b/gradio/themes/default.py index 8f5dd2d5a1..b1c4f35158 100644 --- a/gradio/themes/default.py +++ b/gradio/themes/default.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import Iterable +from collections.abc import Iterable from gradio.themes.base import Base from gradio.themes.utils import colors, fonts, sizes diff --git a/gradio/themes/glass.py b/gradio/themes/glass.py index ec43ac6966..10f5d0c1b5 100644 --- a/gradio/themes/glass.py +++ b/gradio/themes/glass.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import Iterable +from collections.abc import Iterable from gradio.themes.base import Base from gradio.themes.utils import colors, fonts, sizes diff --git a/gradio/themes/monochrome.py b/gradio/themes/monochrome.py index b0911953d9..b62604cbf9 100644 --- a/gradio/themes/monochrome.py +++ b/gradio/themes/monochrome.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import Iterable +from collections.abc import Iterable from gradio.themes.base import Base from gradio.themes.utils import colors, fonts, sizes diff --git a/gradio/themes/soft.py b/gradio/themes/soft.py index 5d9e30e57b..493f31ed05 100644 --- a/gradio/themes/soft.py +++ b/gradio/themes/soft.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import Iterable +from collections.abc import Iterable from gradio.themes.base import Base from gradio.themes.utils import colors, fonts, sizes diff --git a/gradio/themes/utils/fonts.py b/gradio/themes/utils/fonts.py index 5525c2c4d5..55da523ecd 100644 --- a/gradio/themes/utils/fonts.py +++ b/gradio/themes/utils/fonts.py @@ -1,7 +1,7 @@ from __future__ import annotations import json -from typing import Iterable +from collections.abc import Iterable class FontEncoder(json.JSONEncoder): diff --git a/gradio/tunneling.py b/gradio/tunneling.py index b73602bffd..43f75bca27 100644 --- a/gradio/tunneling.py +++ b/gradio/tunneling.py @@ -9,12 +9,11 @@ import time import warnings from pathlib import Path -from typing import List import httpx VERSION = "0.2" -CURRENT_TUNNELS: List["Tunnel"] = [] +CURRENT_TUNNELS: list["Tunnel"] = [] machine = platform.machine() if machine == "x86_64": diff --git a/gradio/utils.py b/gradio/utils.py index e29e601242..540afd7628 100644 --- a/gradio/utils.py +++ b/gradio/utils.py @@ -27,7 +27,7 @@ import warnings from abc import ABC, abstractmethod from collections import OrderedDict -from collections.abc import MutableMapping +from collections.abc import Callable, Iterable, Iterator, MutableMapping, Sequence from contextlib import contextmanager from functools import wraps from io import BytesIO @@ -36,14 +36,9 @@ from typing import ( TYPE_CHECKING, Any, - Callable, Generic, - Iterable, - Iterator, - List, Literal, Optional, - Sequence, TypeVar, ) @@ -511,7 +506,7 @@ def assert_same_components(config1_id, config2_id): raise ValueError(f"{c1} does not match {c2}") def same_children_recursive(children1, chidren2): - for child1, child2 in zip(children1, chidren2): + for child1, child2 in zip(children1, chidren2, strict=False): assert_same_components(child1["id"], child2["id"]) if "children" in child1 or "children" in child2: same_children_recursive(child1["children"], child2["children"]) @@ -520,12 +515,12 @@ def same_children_recursive(children1, chidren2): children2 = config2["layout"]["children"] same_children_recursive(children1, children2) - for d1, d2 in zip(config1["dependencies"], config2["dependencies"]): - for t1, t2 in zip(d1.pop("targets"), d2.pop("targets")): + for d1, d2 in zip(config1["dependencies"], config2["dependencies"], strict=False): + for t1, t2 in zip(d1.pop("targets"), d2.pop("targets"), strict=False): assert_same_components(t1[0], t2[0]) - for i1, i2 in zip(d1.pop("inputs"), d2.pop("inputs")): + for i1, i2 in zip(d1.pop("inputs"), d2.pop("inputs"), strict=False): assert_same_components(i1, i2) - for o1, o2 in zip(d1.pop("outputs"), d2.pop("outputs")): + for o1, o2 in zip(d1.pop("outputs"), d2.pop("outputs"), strict=False): assert_same_components(o1, o2) if d1 != d2: @@ -665,8 +660,7 @@ async def __anext__(self): async def async_iteration(iterator): - # anext not introduced until 3.10 :( - return await iterator.__anext__() + return await anext(iterator) @contextmanager @@ -899,55 +893,13 @@ def get_cancelled_fn_indices( def get_type_hints(fn): - # Importing gradio with the canonical abbreviation. Used in typing._eval_type. - import gradio as gr # noqa: F401 - from gradio import OAuthProfile, OAuthToken, Request # noqa: F401 - if inspect.isfunction(fn) or inspect.ismethod(fn): pass elif callable(fn): fn = fn.__call__ else: return {} - - try: - return typing.get_type_hints(fn) - except TypeError: - # On Python 3.9 or earlier, get_type_hints throws a TypeError if the function - # has a type annotation that include "|". We resort to parsing the signature - # manually using inspect.signature. - type_hints = {} - sig = inspect.signature(fn) - for name, param in sig.parameters.items(): - if param.annotation is inspect.Parameter.empty: - continue - if param.annotation in ["gr.OAuthProfile | None", "None | gr.OAuthProfile"]: - # Special case: we want to inject the OAuthProfile value even on Python 3.9 - type_hints[name] = Optional[OAuthProfile] - if param.annotation == ["gr.OAuthToken | None", "None | gr.OAuthToken"]: - # Special case: we want to inject the OAuthToken value even on Python 3.9 - type_hints[name] = Optional[OAuthToken] - if param.annotation in [ - "gr.Request | None", - "Request | None", - "None | gr.Request", - "None | Request", - ]: - # Special case: we want to inject the Request value even on Python 3.9 - type_hints[name] = Optional[Request] - if "|" in str(param.annotation): - continue - # To convert the string annotation to a class, we use the - # internal typing._eval_type function. This is not ideal, but - # it's the only way to do it without eval-ing the string. - # Since the API is internal, it may change in the future. - try: - type_hints[name] = typing._eval_type( # type: ignore - typing.ForwardRef(param.annotation), globals(), locals() - ) - except (NameError, TypeError): - pass - return type_hints + return typing.get_type_hints(fn) def is_special_typed_parameter(name, parameter_types): @@ -1473,7 +1425,7 @@ def as_list(self): def safe_join(directory: DeveloperPath, path: UserProvidedPath) -> str: """Safely path to a base directory to avoid escaping the base directory. Borrowed from: werkzeug.security.safe_join""" - _os_alt_seps: List[str] = [ + _os_alt_seps: list[str] = [ sep for sep in [os.path.sep, os.path.altsep] if sep is not None and sep != "/" ] diff --git a/pyproject.toml b/pyproject.toml index bcd2989597..1aade61c27 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,7 +11,7 @@ name = "gradio" dynamic = ["version", "dependencies", "optional-dependencies", "readme"] description = "Python library for easily interacting with trained machine learning models" license = "Apache-2.0" -requires-python = ">=3.8" +requires-python = ">=3.10" authors = [ { name = "Abubakar Abid", email = "gradio-team@huggingface.co" }, { name = "Ali Abid", email = "gradio-team@huggingface.co" }, @@ -138,6 +138,7 @@ ignore = [ "B017", # pytest.raises considered evil "B023", # function definition in loop (TODO: un-ignore this) "B028", # explicit stacklevel for warnings + "B905", # zip without explicit `strict` argument "C901", # function is too complex (TODO: un-ignore this) "E501", # from scripts/lint_backend.sh "PLR091", # complexity rules @@ -147,6 +148,7 @@ ignore = [ "SIM117", # multiple nested with blocks (doesn't look good with gr.Row etc) "UP006", # use `list` instead of `List` for type annotations (fails for 3.8) "UP007", # use X | Y for type annotations (TODO: can be enabled once Pydantic plays nice with them) + "UP038", # uses of isinstance and issubclass that take a tuple of types for comparison. ] [tool.ruff.lint.per-file-ignores] diff --git a/test/test_api_info.py b/test/test_api_info.py index 9da72ce4c8..18ce52e60e 100644 --- a/test/test_api_info.py +++ b/test/test_api_info.py @@ -2,7 +2,7 @@ from datetime import datetime, timedelta from enum import Enum from pathlib import Path -from typing import ClassVar, Dict, List, Literal, Optional, Set, Tuple, Union +from typing import ClassVar, Literal, Optional, Union from uuid import UUID import pytest @@ -31,19 +31,19 @@ class FloatModel(GradioModel): class ListModel(GradioModel): - items: List[int] + items: list[int] answer: ClassVar = "Dict(items: List[int])" class DictModel(GradioModel): - data_dict: Dict[str, int] + data_dict: dict[str, int] answer: ClassVar = "Dict(data_dict: Dict(str, int))" class DictModel2(GradioModel): - data_dict: Dict[str, List[float]] + data_dict: dict[str, list[float]] answer: ClassVar = "Dict(data_dict: Dict(str, List[float]))" @@ -83,7 +83,7 @@ class RootWithNestedModel(GradioModel): class LessNestedModel(GradioModel): nested_int: int nested_enum: ColorEnum - nested_dict: Dict[str, List[Union[int, float]]] + nested_dict: dict[str, list[Union[int, float]]] answer: ClassVar = "Dict(nested_int: int, nested_enum: Literal['red', 'green', 'blue'], nested_dict: Dict(str, List[int | float]))" @@ -95,7 +95,7 @@ class StatusModel(GradioModel): class PointModel(GradioRootModel): - root: Tuple[float, float] + root: tuple[float, float] answer: ClassVar = "Tuple[float, float]" @@ -139,7 +139,7 @@ class DateTimeModel(GradioModel): class SetModel(GradioModel): - unique_numbers: Set[int] + unique_numbers: set[int] answer: ClassVar = "Dict(unique_numbers: List[int])" @@ -150,7 +150,7 @@ class ItemModel(GradioModel): class OrderModel(GradioModel): - items: List[ItemModel] + items: list[ItemModel] answer: ClassVar = "Dict(items: List[Dict(name: str, price: float)])" @@ -168,7 +168,7 @@ class CartItemModel(GradioModel): class ShoppingCartModel(GradioModel): - items: List[CartItemModel] + items: list[CartItemModel] answer: ClassVar = "Dict(items: List[Dict(product_name: str, quantity: int, price_per_unit: float)])" @@ -179,13 +179,13 @@ class CoordinateModel(GradioModel): class TupleListModel(GradioModel): - data: List[Tuple[int, str]] + data: list[tuple[int, str]] answer: ClassVar = "Dict(data: List[Tuple[int, str]]" class PathListModel(GradioModel): - file_paths: List[Path] + file_paths: list[Path] answer: ClassVar = "Dict(file_paths: List[str])" @@ -193,7 +193,7 @@ class PathListModel(GradioModel): class PostModel(GradioModel): author: str content: str - tags: List[str] + tags: list[str] likes: int = 0 answer: ClassVar = "Dict(author: str, content: str, tags: List[str], likes: int)" @@ -203,7 +203,7 @@ class PostModel(GradioModel): class NamedTupleDictionaryModel(GradioModel): - people: Dict[str, Person] + people: dict[str, Person] answer: ClassVar = "Dict(people: Dict(str, Tuple[Any, Any]))" diff --git a/test/test_blocks.py b/test/test_blocks.py index 2ce1f76bab..4c19188e5b 100644 --- a/test/test_blocks.py +++ b/test/test_blocks.py @@ -552,7 +552,8 @@ def process_and_dump(component): return output assert all( - o["value"] == process_and_dump(c) for o, c in zip(output, io_components) + o["value"] == process_and_dump(c) + for o, c in zip(output, io_components, strict=False) ) @pytest.mark.asyncio @@ -1011,7 +1012,9 @@ def generator(x): class TestBatchProcessing: def test_raise_exception_if_batching_an_event_thats_not_queued(self): def trim(words, lens): - trimmed_words = [word[: int(length)] for word, length in zip(words, lens)] + trimmed_words = [ + word[: int(length)] for word, length in zip(words, lens, strict=False) + ] return [trimmed_words] msg = "In order to use batching, the queue must be enabled." @@ -1062,7 +1065,7 @@ def regular_fn(word1, word2): def batch_fn(words, lengths): comparisons = [] trim_words = [] - for word, length in zip(words, lengths): + for word, length in zip(words, lengths, strict=False): trim_words.append(word[:length]) comparisons.append(len(word) > length) return trim_words, comparisons @@ -1137,7 +1140,7 @@ async def test_unequal_batch_sizes(self): def batch_fn(x, y): results = [] - for word1, word2 in zip(x, y): + for word1, word2 in zip(x, y, strict=False): results.append(f"Hello {word1}{word2}") return (results,) diff --git a/test/test_helpers.py b/test/test_helpers.py index af4bdab7d4..df1a70f1f7 100644 --- a/test/test_helpers.py +++ b/test/test_helpers.py @@ -458,7 +458,9 @@ def many_missing(a, b, c): def test_caching_with_batch(self, patched_cache_folder): def trim_words(words, lens): - trimmed_words = [word[:length] for word, length in zip(words, lens)] + trimmed_words = [ + word[:length] for word, length in zip(words, lens, strict=False) + ] return [trimmed_words] io = gr.Interface( @@ -475,7 +477,9 @@ def trim_words(words, lens): def test_caching_with_batch_multiple_outputs(self, patched_cache_folder): def trim_words(words, lens): - trimmed_words = [word[:length] for word, length in zip(words, lens)] + trimmed_words = [ + word[:length] for word, length in zip(words, lens, strict=False) + ] return trimmed_words, lens io = gr.Interface( diff --git a/test/test_processing_utils.py b/test/test_processing_utils.py index 96fa39184e..7961b71c67 100644 --- a/test/test_processing_utils.py +++ b/test/test_processing_utils.py @@ -284,11 +284,12 @@ def raise_ffmpy_runtime_exception(*args, **kwargs): def test_video_has_playable_codecs_catches_exceptions( self, exception_to_raise, test_file_dir ): - with patch( - "ffmpy.FFprobe.run", side_effect=exception_to_raise - ), tempfile.NamedTemporaryFile( - suffix="out.avi", delete=False - ) as tmp_not_playable_vid: + with ( + patch("ffmpy.FFprobe.run", side_effect=exception_to_raise), + tempfile.NamedTemporaryFile( + suffix="out.avi", delete=False + ) as tmp_not_playable_vid, + ): shutil.copy( str(test_file_dir / "bad_video_sample.mp4"), tmp_not_playable_vid.name, diff --git a/test/test_reload.py b/test/test_reload.py index 6bd65ae09a..032e7a0ea5 100644 --- a/test/test_reload.py +++ b/test/test_reload.py @@ -1,6 +1,5 @@ import dataclasses from pathlib import Path -from typing import List import pytest @@ -21,7 +20,7 @@ def build_demo(): class Config: module_name: str path: Path - watch_dirs: List[str] + watch_dirs: list[str] demo_name: str diff --git a/test/test_routes.py b/test/test_routes.py index 01ec7fc710..46c8c1b809 100644 --- a/test/test_routes.py +++ b/test/test_routes.py @@ -6,7 +6,6 @@ import time from contextlib import asynccontextmanager, closing from pathlib import Path -from typing import Dict from unittest.mock import patch import gradio_client as grc @@ -1202,7 +1201,7 @@ def test_get_root_url( ], ) def test_get_root_url_headers( - headers: Dict[str, str], root_path: str, route_path: str, expected_root_url: str + headers: dict[str, str], root_path: str, route_path: str, expected_root_url: str ): scope = { "type": "http", diff --git a/test/test_utils.py b/test/test_utils.py index f8662ff1f7..886e928b92 100644 --- a/test/test_utils.py +++ b/test/test_utils.py @@ -4,15 +4,15 @@ import os import sys import warnings +from collections.abc import Sequence from pathlib import Path -from typing import Sequence +from typing import Literal from unittest.mock import MagicMock, patch import numpy as np import pytest from hypothesis import given, settings from hypothesis import strategies as st -from typing_extensions import Literal from gradio import EventData, Request from gradio.external_utils import format_ner_list