Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove manual ip address check and launch counter #8884

Merged
merged 15 commits into from
Jul 29, 2024
5 changes: 5 additions & 0 deletions .changeset/weak-glasses-enter.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"gradio": minor
---

feat:replace ip addresses with machine-specific hashes
52 changes: 2 additions & 50 deletions gradio/analytics.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@

import gradio
from gradio import wasm_utils
from gradio.context import Context
from gradio.utils import core_gradio_components, get_package_version

# For testability, we import the pyfetch function into this module scope and define a fallback coroutine object to be patched in tests.
Expand Down Expand Up @@ -67,7 +66,7 @@ def _do_analytics_request(topic: str, data: dict[str, Any]) -> None:


def _do_normal_analytics_request(topic: str, data: dict[str, Any]) -> None:
data["ip_address"] = get_local_ip_address()
data["ip_address"] = ""
try:
_send_telemetry_in_thread(
topic=topic,
Expand All @@ -80,7 +79,7 @@ def _do_normal_analytics_request(topic: str, data: dict[str, Any]) -> None:


async def _do_wasm_analytics_request(url: str, data: dict[str, Any]) -> None:
data["ip_address"] = await get_local_ip_address_wasm()
data["ip_address"] = ""

# We use urllib.parse.urlencode to encode the data as a form.
# Ref: https://docs.python.org/3/library/urllib.request.html#urllib-examples
Expand Down Expand Up @@ -116,53 +115,6 @@ def version_check():
pass


def get_local_ip_address() -> str:
"""
Gets the public IP address or returns the string "No internet connection" if unable
to obtain it or the string "Analytics disabled" if a user has disabled analytics.
Does not make a new request if the IP address has already been obtained in the
same Python session.
"""
if not analytics_enabled():
return "Analytics disabled"

if Context.ip_address is None:
try:
ip_address = httpx.get(
"https://checkip.amazonaws.com/", timeout=3
).text.strip()
except (httpx.ConnectError, httpx.ReadTimeout):
ip_address = "No internet connection"
Context.ip_address = ip_address
else:
ip_address = Context.ip_address
return ip_address


async def get_local_ip_address_wasm() -> str:
"""The Wasm-compatible version of get_local_ip_address()."""
if not analytics_enabled():
return "Analytics disabled"

if Context.ip_address is None:
try:
response = await asyncio.wait_for(
pyodide_pyfetch(
# The API used by the normal version (`get_local_ip_address()`), `https://checkip.amazonaws.com/``, blocks CORS requests, so here we use a different API.
"https://api.ipify.org"
),
timeout=5,
)
response_text: str = await response.string() # type: ignore
ip_address = response_text.strip()
except (asyncio.TimeoutError, OSError):
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:
if not analytics_enabled():
return
Expand Down
1 change: 0 additions & 1 deletion gradio/blocks.py
Original file line number Diff line number Diff line change
Expand Up @@ -2392,7 +2392,6 @@ def reverse(text):
# So we need to manually cancel them. See `self.close()`..
self.startup_events()

utils.launch_counter()
self.is_sagemaker = utils.sagemaker_check()
if share is None:
if self.is_colab:
Expand Down
1 change: 0 additions & 1 deletion gradio/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ 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 # The IP address of the user.
hf_token: str | None = None # The token provided when loading private HF repos


Expand Down
22 changes: 0 additions & 22 deletions gradio/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,20 +51,16 @@
from gradio_client.documentation import document
from typing_extensions import ParamSpec

import gradio
from gradio.context import get_blocks_context
from gradio.data_classes import BlocksConfigDict, FileData
from gradio.exceptions import Error
from gradio.strings import en

if TYPE_CHECKING: # Only import for type checking (is False at runtime).
from gradio.blocks import BlockContext, Blocks
from gradio.components import Component
from gradio.routes import App, Request
from gradio.state_holder import SessionState

JSON_PATH = os.path.join(os.path.dirname(gradio.__file__), "launches.json")

P = ParamSpec("P")
T = TypeVar("T")

Expand Down Expand Up @@ -438,24 +434,6 @@ def download_if_url(article: str) -> str:
return article


def launch_counter() -> None:
try:
if not os.path.exists(JSON_PATH):
launches = {"launches": 1}
with open(JSON_PATH, "w+", encoding="utf-8") as j:
json.dump(launches, j)
else:
with open(JSON_PATH, encoding="utf-8") as j:
launches = json.load(j)
launches["launches"] += 1
if launches["launches"] in [25, 50, 150, 500, 1000]:
print(en["BETA_INVITE"])
with open(JSON_PATH, "w", encoding="utf-8") as j:
j.write(json.dumps(launches))
except Exception:
pass


def get_default_args(func: Callable) -> list[Any]:
signature = inspect.signature(func)
return [
Expand Down
26 changes: 0 additions & 26 deletions test/test_analytics.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
import asyncio
import ipaddress
import json
import os
import warnings
from unittest.mock import patch

import httpx
import pytest

from gradio import analytics, wasm_utils
from gradio.context import Context

os.environ["GRADIO_ANALYTICS_ENABLED"] = "False"

Expand Down Expand Up @@ -62,26 +59,3 @@ async def test_error_analytics_successful_in_wasm_mode(
await asyncio.wait(all_tasks)

pyodide_pyfetch.assert_called()


class TestIPAddress:
@pytest.mark.flaky
def test_get_ip(self):
Context.ip_address = None
ip = analytics.get_local_ip_address()
if ip in ("No internet connection", "Analytics disabled"):
return
ipaddress.ip_address(ip)

@patch("httpx.get")
def test_get_ip_without_internet(self, mock_get, monkeypatch):
mock_get.side_effect = httpx.ConnectError("Connection error")
monkeypatch.setenv("GRADIO_ANALYTICS_ENABLED", "True")
Context.ip_address = None
ip = analytics.get_local_ip_address()
assert ip == "No internet connection"

monkeypatch.setenv("GRADIO_ANALYTICS_ENABLED", "False")
Context.ip_address = None
ip = analytics.get_local_ip_address()
assert ip == "Analytics disabled"