diff --git a/CHANGELOG.md b/CHANGELOG.md index 840cecdb48..07279e3a94 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -69,6 +69,8 @@ No changes to highlight. ## Full Changelog: * Rewrote frontend using CSS variables for themes by [@pngwn](https://github.com/pngwn) in [PR 2840](https://github.com/gradio-app/gradio/pull/2840) +* Moved telemetry requests to run on background threads by [@abidlabs](https://github.com/abidlabs) in [PR 3054](https://github.com/gradio-app/gradio/pull/3054) + ## Contributors Shoutout: No changes to highlight. diff --git a/gradio/blocks.py b/gradio/blocks.py index 12aed79702..de88893269 100644 --- a/gradio/blocks.py +++ b/gradio/blocks.py @@ -496,7 +496,6 @@ class Blocks(BlockContext): self.height = None self.api_open = True - self.ip_address = "" self.is_space = True if os.getenv("SYSTEM") == "spaces" else False self.favicon_path = None self.auth = None @@ -515,10 +514,8 @@ class Blocks(BlockContext): self.progress_tracking = None if self.analytics_enabled: - self.ip_address = utils.get_local_ip_address() data = { "mode": self.mode, - "ip_address": self.ip_address, "custom_css": self.css is not None, "theme": self.theme, "version": (pkgutil.get_data(__name__, "version.txt") or b"") @@ -1451,7 +1448,7 @@ class Blocks(BlockContext): print(strings.en["SHARE_LINK_MESSAGE"]) except RuntimeError: if self.analytics_enabled: - utils.error_analytics(self.ip_address, "Not able to set up tunnel") + utils.error_analytics("Not able to set up tunnel") self.share_url = None self.share = False print(strings.en["COULD_NOT_GET_SHARE_LINK"]) @@ -1534,7 +1531,6 @@ class Blocks(BlockContext): "is_google_colab": self.is_colab, "is_sharing_on": self.share, "share_url": self.share_url, - "ip_address": self.ip_address, "enable_queue": self.enable_queue, "show_tips": self.show_tips, "server_name": server_name, diff --git a/gradio/context.py b/gradio/context.py index 8eeb73d95a..58ec371139 100644 --- a/gradio/context.py +++ b/gradio/context.py @@ -12,3 +12,4 @@ class Context: root_block: Blocks | None = None # The current root block that holds all blocks. block: BlockContext | None = None # The current block that children are added to. id: int = 0 # Running id to uniquely refer to any block that gets defined + ip_address: str | None = None diff --git a/gradio/interface.py b/gradio/interface.py index 72e3851082..f48345d17a 100644 --- a/gradio/interface.py +++ b/gradio/interface.py @@ -389,7 +389,6 @@ class Interface(Blocks): "inputs": inputs, "outputs": outputs, "live": live, - "ip_address": self.ip_address, "interpretation": interpretation, "allow_flagging": allow_flagging, "custom_css": self.css is not None, diff --git a/gradio/strings.py b/gradio/strings.py index 03ee329e7a..d4263b0d29 100644 --- a/gradio/strings.py +++ b/gradio/strings.py @@ -1,4 +1,6 @@ import json +import threading +from typing import Dict import requests @@ -30,12 +32,17 @@ en = { ], } -try: - updated_messaging = requests.get(MESSAGING_API_ENDPOINT, timeout=3).json() - en.update(updated_messaging) -except ( - requests.ConnectionError, - requests.exceptions.ReadTimeout, - json.decoder.JSONDecodeError, -): # Use default messaging - pass + +def get_updated_messaging(en: Dict): + try: + updated_messaging = requests.get(MESSAGING_API_ENDPOINT, timeout=3).json() + en.update(updated_messaging) + except ( + requests.ConnectionError, + requests.exceptions.ReadTimeout, + json.decoder.JSONDecodeError, + ): # Use default messaging + pass + + +threading.Thread(target=get_updated_messaging, args=(en,)).start() diff --git a/gradio/utils.py b/gradio/utils.py index 7dd603b2e1..674154f4c3 100644 --- a/gradio/utils.py +++ b/gradio/utils.py @@ -12,6 +12,7 @@ import pkgutil import random import re import sys +import threading import time import typing import warnings @@ -84,58 +85,83 @@ def version_check(): def get_local_ip_address() -> str: - """Gets the public IP address or returns the string "No internet connection" if unable to obtain it.""" - try: - ip_address = requests.get( - "https://checkip.amazonaws.com/", timeout=3 - ).text.strip() - except (requests.ConnectionError, requests.exceptions.ReadTimeout): - ip_address = "No internet connection" + """Gets the public IP address or returns the string "No internet connection" if unable to obtain it. Does not make a new request if the IP address has already been obtained.""" + if Context.ip_address is None: + try: + ip_address = requests.get( + "https://checkip.amazonaws.com/", timeout=3 + ).text.strip() + except (requests.ConnectionError, requests.exceptions.ReadTimeout): + ip_address = "No internet connection" + Context.ip_address = ip_address + else: + ip_address = Context.ip_address return ip_address def initiated_analytics(data: Dict[str, Any]) -> None: - try: - requests.post( - analytics_url + "gradio-initiated-analytics/", data=data, timeout=3 - ) - except (requests.ConnectionError, requests.exceptions.ReadTimeout): - pass # do not push analytics if no network + data.update({"ip_address": get_local_ip_address()}) + + def initiated_analytics_thread(data: Dict[str, Any]) -> None: + try: + requests.post( + analytics_url + "gradio-initiated-analytics/", data=data, timeout=3 + ) + except (requests.ConnectionError, requests.exceptions.ReadTimeout): + pass # do not push analytics if no network + + threading.Thread(target=initiated_analytics_thread, args=(data,)).start() def launch_analytics(data: Dict[str, Any]) -> None: - try: - requests.post( - analytics_url + "gradio-launched-analytics/", data=data, timeout=3 - ) - except (requests.ConnectionError, requests.exceptions.ReadTimeout): - pass # do not push analytics if no network + data.update({"ip_address": get_local_ip_address()}) + + def launch_analytics_thread(data: Dict[str, Any]) -> None: + try: + requests.post( + analytics_url + "gradio-launched-analytics/", data=data, timeout=3 + ) + except (requests.ConnectionError, requests.exceptions.ReadTimeout): + pass # do not push analytics if no network + + threading.Thread(target=launch_analytics_thread, args=(data,)).start() def integration_analytics(data: Dict[str, Any]) -> None: - try: - requests.post( - analytics_url + "gradio-integration-analytics/", data=data, timeout=3 - ) - except (requests.ConnectionError, requests.exceptions.ReadTimeout): - pass # do not push analytics if no network + data.update({"ip_address": get_local_ip_address()}) + + def integration_analytics_thread(data: Dict[str, Any]) -> None: + try: + requests.post( + analytics_url + "gradio-integration-analytics/", data=data, timeout=3 + ) + except (requests.ConnectionError, requests.exceptions.ReadTimeout): + pass # do not push analytics if no network + + threading.Thread(target=integration_analytics_thread, args=(data,)).start() -def error_analytics(ip_address: str, message: str) -> None: +def error_analytics(message: str) -> None: """ Send error analytics if there is network :param ip_address: IP address where error occurred :param message: Details about error """ - data = {"ip_address": ip_address, "error": message} - try: - requests.post(analytics_url + "gradio-error-analytics/", data=data, timeout=3) - except (requests.ConnectionError, requests.exceptions.ReadTimeout): - pass # do not push analytics if no network + data = {"ip_address": get_local_ip_address(), "error": message} + + def error_analytics_thread(data: Dict[str, Any]) -> None: + try: + requests.post( + analytics_url + "gradio-error-analytics/", data=data, timeout=3 + ) + except (requests.ConnectionError, requests.exceptions.ReadTimeout): + pass # do not push analytics if no network + + threading.Thread(target=error_analytics_thread, args=(data,)).start() -async def log_feature_analytics(ip_address: str, feature: str) -> None: - data = {"ip_address": ip_address, "feature": feature} +async def log_feature_analytics(feature: str) -> None: + data = {"ip_address": get_local_ip_address(), "feature": feature} async with aiohttp.ClientSession() as session: try: async with session.post( diff --git a/test/test_utils.py b/test/test_utils.py index 0f467a5d8e..9b4de02672 100644 --- a/test/test_utils.py +++ b/test/test_utils.py @@ -12,6 +12,7 @@ from httpx import AsyncClient, Response from pydantic import BaseModel from typing_extensions import Literal +from gradio.context import Context from gradio.test_data.blocks_configs import ( XRAY_CONFIG, XRAY_CONFIG_DIFF_IDS, @@ -65,12 +66,12 @@ class TestUtils: @mock.patch("requests.post") def test_error_analytics_doesnt_crash_on_connection_error(self, mock_post): mock_post.side_effect = requests.ConnectionError() - error_analytics("placeholder", "placeholder") + error_analytics("placeholder") mock_post.assert_called() @mock.patch("requests.post") def test_error_analytics_successful(self, mock_post): - error_analytics("placeholder", "placeholder") + error_analytics("placeholder") mock_post.assert_called() @mock.patch("requests.post") @@ -106,6 +107,7 @@ class TestUtils: class TestIPAddress: @pytest.mark.flaky def test_get_ip(self): + Context.ip_address = None ip = get_local_ip_address() if ip == "No internet connection": return @@ -113,6 +115,7 @@ class TestIPAddress: @mock.patch("requests.get") def test_get_ip_without_internet(self, mock_get): + Context.ip_address = None mock_get.side_effect = requests.ConnectionError() ip = get_local_ip_address() assert ip == "No internet connection"