Repository: gradio-app/gradio

Issue:
gr.update on multiple output components errors when output created using list multiplication but succeeds with list comprehension
### Describe the bug

`[gr.update(visible=value) for _ in range(2)]` works.
`[gr.update(visible=value)]*2` gives error below.

Toggle the checkbox to trigger.

I printed in the function body to check differences in the 2 variants. They look the same.
```
True
variant1: [{'visible': False, '__type__': 'generic_update'}, {'visible': False, '__type__': 'generic_update'}]
variant2: [{'visible': False, '__type__': 'generic_update'}, {'visible': False, '__type__': 'generic_update'}]
```

I tried debugging the python file and see that the failure variant can stop on my breakpoint at `gradio/components.py", line 1534, in postprocess` but the successful variant does not stop at the breakpoint? 
Why is the successful variant working? And why is it not stopping at my breakpoint like the failure variant does? Are they going down different code paths?

### Is there an existing issue for this?

- [X] I have searched the existing issues

### Reproduction

```
import gradio as gr

num_images = 2

with gr.Blocks() as demo:
    im_list = [gr.Image() for i in range(num_images)]
    def change_visibility(value):
        variant1 = [gr.update(visible=value) for _ in range(2)]
        variant2 = [gr.update(visible=value)]*2
        
        print(variant1 == variant2)
        print('variant1:', variant1)
        print('variant2:', variant2)
        
        # return variant1
        return variant2
    
    checkbox = gr.Checkbox(value=True, label='Show image1')
    checkbox.change(change_visibility, inputs=checkbox,outputs=im_list) 
    
if __name__ == "__main__":
    demo.launch()
```

### Screenshot

_No response_

### Logs

```shell
Traceback (most recent call last):
  File "/Users/hanqi/code/ahrefs/venv/lib/python3.8/site-packages/gradio/routes.py", line 384, in run_predict
    output = await app.get_blocks().process_api(
  File "/Users/hanqi/code/ahrefs/venv/lib/python3.8/site-packages/gradio/blocks.py", line 1027, in process_api
    data = self.postprocess_data(fn_index, result["prediction"], state)
  File "/Users/hanqi/code/ahrefs/venv/lib/python3.8/site-packages/gradio/blocks.py", line 964, in postprocess_data
    prediction_value = block.postprocess(prediction_value)
  File "/Users/hanqi/code/ahrefs/venv/lib/python3.8/site-packages/gradio/components.py", line 1534, in postprocess
    raise ValueError("Cannot process this value as an Image")
ValueError: Cannot process this value as an Image
```
```


### System Info

```shell
Python 3.8.12 (default, Dec  8 2021, 11:22:00) 
[Clang 12.0.5 (clang-1205.0.22.11)] 
Gradio version 3.19.1
MacOS Big Sur version 11.3
```


### Severity

annoying


Discussion:
So strange, I can confirm the issue but have no idea what could be causing this! We'll look into it, thanks for reporting @gitgithan!
Thanks for filing @gitgithan !

The [prostprocessing](https://github.com/gradio-app/gradio/blob/main/gradio/blocks.py#L360) of update dictionaries involves deleting keys from the dictionary, which is why shallow copies will not work as expected. 

Maybe this can be rewritten to not modify the dictionaries in place. 
Ah nice catch @freddyaboulton! That should fix it. @gitgithan would you be interested in making a PR? 
@abidlabs Yes I'm interested in looking at how to fix this, it has caused me immeasurable pain today.
Could I get some guidance on how to trace the execution so I understand what's going on? 
I assume I'll need to know the code paths for both variants to compare differences, and to ensure relevant files are considered/editted.
@freddyaboulton 's comment and link seems to have dived straight into the source of the issue but I only have 1 day experience on Gradio so far so it's over my head
Thanks for wanting to take a look at this @gitgithan !

The logic for processing the user defined functions in the backend is pretty much handled entirely by [process_api](https://github.com/gradio-app/gradio/blob/main/gradio/blocks.py#L969).

There are three parts:
1. Preprocessing the data from the front-end
2. Calling the user defined function with this data
3. post processing the data returned from the function

If you want to wrap your head around this, I would set a `breakpoint()` in `process_api` and then launch a simple demo and step through it.

I think this particular problem is caused in this [part](https://github.com/gradio-app/gradio/blob/main/gradio/blocks.py#L955) of the postprocessing, where we apply special processing for update dictionaries. 

I think just modifying that function to return a new copy of the update dict with the keys removed, as opposed to removing the keys in-place should fix the issue.

Resolving patch:
diff --git a/gradio/blocks.py b/gradio/blocks.py
--- a/gradio/blocks.py
+++ b/gradio/blocks.py
@@ -4,7 +4,6 @@
 import inspect
 import json
 import os
-import pkgutil
 import random
 import secrets
 import sys
@@ -30,6 +29,7 @@
 from gradio.themes import ThemeClass as Theme
 from gradio.tunneling import CURRENT_TUNNELS
 from gradio.utils import (
+    GRADIO_VERSION,
     TupleNoPrint,
     check_function_inputs_match,
     component_or_layout_class,
@@ -40,7 +40,6 @@
 
 set_documentation_group("blocks")
 
-
 if TYPE_CHECKING:  # Only import for type checking (is False at runtime).
     import comet_ml
     from fastapi.applications import FastAPI
@@ -485,7 +484,8 @@ def __init__(
             if analytics_enabled is not None
             else os.getenv("GRADIO_ANALYTICS_ENABLED", "True") == "True"
         )
-
+        if not self.analytics_enabled:
+            os.environ["HF_HUB_DISABLE_TELEMETRY"] = "True"
         super().__init__(render=False, **kwargs)
         self.blocks: Dict[int, Block] = {}
         self.fns: List[BlockFunction] = []
@@ -522,9 +522,8 @@ def __init__(
             data = {
                 "mode": self.mode,
                 "custom_css": self.css is not None,
-                "version": (pkgutil.get_data(__name__, "version.txt") or b"")
-                .decode("ascii")
-                .strip(),
+                "theme": self.theme,
+                "version": GRADIO_VERSION,
             }
             utils.initiated_analytics(data)
 
@@ -1579,6 +1578,7 @@ def reverse(text):
                 "mode": self.mode,
             }
             utils.launch_analytics(data)
+            utils.launched_telemetry(self, data)
 
         utils.show_tip(self)
 
diff --git a/gradio/interface.py b/gradio/interface.py
--- a/gradio/interface.py
+++ b/gradio/interface.py
@@ -8,7 +8,6 @@
 import inspect
 import json
 import os
-import pkgutil
 import re
 import warnings
 import weakref
@@ -31,6 +30,7 @@
 from gradio.layouts import Column, Row, Tab, Tabs
 from gradio.pipelines import load_from_pipeline
 from gradio.themes import ThemeClass as Theme
+from gradio.utils import GRADIO_VERSION
 
 set_documentation_group("interface")
 
@@ -316,13 +316,8 @@ def clean_html(raw_html):
 
         self.simple_server = None
 
-        # For analytics_enabled and allow_flagging: (1) first check for
-        # parameter, (2) check for env variable, (3) default to True/"manual"
-        self.analytics_enabled = (
-            analytics_enabled
-            if analytics_enabled is not None
-            else os.getenv("GRADIO_ANALYTICS_ENABLED", "True") == "True"
-        )
+        # For allow_flagging: (1) first check for parameter,
+        # (2) check for env variable, (3) default to True/"manual"
         if allow_flagging is None:
             allow_flagging = os.getenv("GRADIO_ALLOW_FLAGGING", "manual")
         if allow_flagging is True:
@@ -388,9 +383,8 @@ def clean_html(raw_html):
                 "interpretation": interpretation,
                 "allow_flagging": allow_flagging,
                 "custom_css": self.css is not None,
-                "version": (pkgutil.get_data(__name__, "version.txt") or b"")
-                .decode("ascii")
-                .strip(),
+                "theme": self.theme,
+                "version": GRADIO_VERSION,
             }
             utils.initiated_analytics(data)
 
diff --git a/gradio/utils.py b/gradio/utils.py
--- a/gradio/utils.py
+++ b/gradio/utils.py
@@ -41,6 +41,7 @@
 import httpx
 import matplotlib.pyplot as plt
 import requests
+from huggingface_hub.utils import send_telemetry
 from markdown_it import MarkdownIt
 from mdit_py_plugins.dollarmath.index import dollarmath_plugin
 from mdit_py_plugins.footnote.index import footnote_plugin
@@ -57,6 +58,9 @@
 analytics_url = "https://api.gradio.app/"
 PKG_VERSION_URL = "https://api.gradio.app/pkg-version"
 JSON_PATH = os.path.join(os.path.dirname(gradio.__file__), "launches.json")
+GRADIO_VERSION = (
+    (pkgutil.get_data(__name__, "version.txt") or b"").decode("ascii").strip()
+)
 
 T = TypeVar("T")
 
@@ -113,7 +117,19 @@ def initiated_analytics_thread(data: Dict[str, Any]) -> None:
         except (requests.ConnectionError, requests.exceptions.ReadTimeout):
             pass  # do not push analytics if no network
 
+    def initiated_telemetry_thread(data: Dict[str, Any]) -> None:
+        try:
+            send_telemetry(
+                topic="gradio/initiated",
+                library_name="gradio",
+                library_version=GRADIO_VERSION,
+                user_agent=data,
+            )
+        except Exception:
+            pass
+
     threading.Thread(target=initiated_analytics_thread, args=(data,)).start()
+    threading.Thread(target=initiated_telemetry_thread, args=(data,)).start()
 
 
 def launch_analytics(data: Dict[str, Any]) -> None:
@@ -130,6 +146,65 @@ def launch_analytics_thread(data: Dict[str, Any]) -> None:
     threading.Thread(target=launch_analytics_thread, args=(data,)).start()
 
 
+def launched_telemetry(blocks: gradio.Blocks, data: Dict[str, Any]) -> None:
+    blocks_telemetry, inputs_telemetry, outputs_telemetry, targets_telemetry = (
+        [],
+        [],
+        [],
+        [],
+    )
+
+    for x in list(blocks.blocks.values()):
+        blocks_telemetry.append(x.get_block_name()) if isinstance(
+            x, BlockContext
+        ) else blocks_telemetry.append(str(x))
+
+    for x in blocks.dependencies:
+        targets_telemetry = targets_telemetry + [
+            str(blocks.blocks[y]) for y in x["targets"]
+        ]
+        inputs_telemetry = inputs_telemetry + [
+            str(blocks.blocks[y]) for y in x["inputs"]
+        ]
+        outputs_telemetry = outputs_telemetry + [
+            str(blocks.blocks[y]) for y in x["outputs"]
+        ]
+    additional_data = {
+        "is_kaggle": blocks.is_kaggle,
+        "is_sagemaker": blocks.is_sagemaker,
+        "using_auth": blocks.auth is not None,
+        "dev_mode": blocks.dev_mode,
+        "show_api": blocks.show_api,
+        "show_error": blocks.show_error,
+        "theme": blocks.theme,
+        "title": blocks.title,
+        "inputs": blocks.input_components
+        if blocks.mode == "interface"
+        else inputs_telemetry,
+        "outputs": blocks.output_components
+        if blocks.mode == "interface"
+        else outputs_telemetry,
+        "targets": targets_telemetry,
+        "blocks": blocks_telemetry,
+        "events": [str(x["trigger"]) for x in blocks.dependencies],
+    }
+
+    data.update(additional_data)
+
+    def launched_telemtry_thread(data: Dict[str, Any]) -> None:
+        try:
+            send_telemetry(
+                topic="gradio/launched",
+                library_name="gradio",
+                library_version=GRADIO_VERSION,
+                user_agent=data,
+            )
+        except Exception as e:
+            print("Error while sending telemetry: {}".format(e))
+
+    threading.Thread(target=launched_telemtry_thread, args=(data,)).start()
+
+
 def integration_analytics(data: Dict[str, Any]) -> None:
     data.update({"ip_address": get_local_ip_address()})
 
@@ -141,7 +216,19 @@ def integration_analytics_thread(data: Dict[str, Any]) -> None:
         except (requests.ConnectionError, requests.exceptions.ReadTimeout):
             pass  # do not push analytics if no network
 
+    def integration_telemetry_thread(data: Dict[str, Any]) -> None:
+        try:
+            send_telemetry(
+                topic="gradio/integration",
+                library_name="gradio",
+                library_version=GRADIO_VERSION,
+                user_agent=data,
+            )
+        except Exception as e:
+            print("Error while sending telemetry: {}".format(e))
+
     threading.Thread(target=integration_analytics_thread, args=(data,)).start()
+    threading.Thread(target=integration_telemetry_thread, args=(data,)).start()
 
 
 def error_analytics(message: str) -> None:
@@ -160,7 +247,19 @@ def error_analytics_thread(data: Dict[str, Any]) -> None:
         except (requests.ConnectionError, requests.exceptions.ReadTimeout):
             pass  # do not push analytics if no network
 
+    def error_telemetry_thread(data: Dict[str, Any]) -> None:
+        try:
+            send_telemetry(
+                topic="gradio/error",
+                library_name="gradio",
+                library_version=GRADIO_VERSION,
+                user_agent=message,
+            )
+        except Exception as e:
+            print("Error while sending telemetry: {}".format(e))
+
     threading.Thread(target=error_analytics_thread, args=(data,)).start()
+    threading.Thread(target=error_telemetry_thread, args=(data,)).start()
 
 
 async def log_feature_analytics(feature: str) -> None:


Test patch:
diff --git a/test/requirements.in b/test/requirements.in
--- a/test/requirements.in
+++ b/test/requirements.in
@@ -11,7 +11,7 @@ scikit-image
 shap
 pytest
 wandb
-huggingface_hub
+huggingface_hub>=0.13.0
 pytest-cov
 pytest-asyncio
 black
diff --git a/test/requirements.txt b/test/requirements.txt
--- a/test/requirements.txt
+++ b/test/requirements.txt
@@ -1,5 +1,5 @@
 #
-# This file is autogenerated by pip-compile with Python 3.10
+# This file is autogenerated by pip-compile with Python 3.9
 # by the following command:
 #
 #    pip-compile --output-file=requirements.txt
@@ -24,8 +24,12 @@ backcall==0.2.0
     # via ipython
 black==22.6.0
     # via -r requirements.in
-boto3==1.26.65    
+boto3==1.26.65
     # via -r requirements.in
+botocore==1.29.87
+    # via
+    #   boto3
+    #   s3transfer
 certifi==2022.6.15
     # via
     #   dulwich
@@ -98,7 +102,7 @@ httpx==0.23.0
     # via
     #   -r requirements.in
     #   respx
-huggingface-hub==0.8.1
+huggingface-hub==0.13.0
     # via
     #   -r requirements.in
     #   transformers
@@ -110,7 +114,9 @@ idna==3.3
 imageio==2.19.5
     # via scikit-image
 importlib-metadata==4.2.0
-    # via mlflow
+    # via
+    #   flask
+    #   mlflow
 iniconfig==1.1.1
     # via pytest
 ipython==7.34.0
@@ -125,6 +131,10 @@ jinja2==3.1.2
     # via
     #   altair
     #   flask
+jmespath==1.0.1
+    # via
+    #   boto3
+    #   botocore
 joblib==1.1.0
     # via scikit-learn
 jsonschema==4.7.2
@@ -245,7 +255,9 @@ pytest-asyncio==0.19.0
 pytest-cov==3.0.0
     # via -r requirements.in
 python-dateutil==2.8.2
-    # via pandas
+    # via
+    #   botocore
+    #   pandas
 pytz==2022.1
     # via
     #   mlflow
@@ -278,6 +290,8 @@ respx==0.19.2
     # via -r requirements.in
 rfc3986[idna2008]==1.5.0
     # via httpx
+s3transfer==0.6.0
+    # via boto3
 scikit-image==0.19.3
     # via -r requirements.in
 scikit-learn==1.0.2
@@ -358,11 +372,14 @@ transformers==4.20.1
     # via -r requirements.in
 typing-extensions==4.3.0
     # via
+    #   black
     #   huggingface-hub
     #   pydantic
+    #   starlette
     #   torch
 urllib3==1.26.10
     # via
+    #   botocore
     #   dulwich
     #   requests
     #   sentry-sdk