From 226e87cd54a1b4cf0340955eac6bc709b6844c0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Radam=C3=A9s=20Ajna?= Date: Tue, 14 Mar 2023 15:15:12 -0700 Subject: [PATCH] implement missing methods handle nested dict (#3459) * implement missing methods handle nested dict test for get 'user-agent' extra example for gr.Requests * missing notebook example * add missing attributes, keys, values, items * update changelog * fix changelog --- CHANGELOG.md | 2 +- demo/request_ip_headers/run.ipynb | 1 + demo/request_ip_headers/run.py | 15 ++++++++++++ gradio/routes.py | 38 +++++++++++++++++++++++++++++-- test/test_routes.py | 21 +++++++++++++++++ 5 files changed, 74 insertions(+), 3 deletions(-) create mode 100644 demo/request_ip_headers/run.ipynb create mode 100644 demo/request_ip_headers/run.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 456a014be7..4b87a282ae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,7 @@ No changes to highlight. ## Bug Fixes: -No changes to highlight. +Fixed issue with `gr.Request` object failing to handle dictionaries when nested keys couldn't be converted to variable names [#3454](https://github.com/gradio-app/gradio/issues/3454) by [@radames](https://github.com/radames) in [PR 3459](https://github.com/gradio-app/gradio/pull/3459) ## Documentation Changes: diff --git a/demo/request_ip_headers/run.ipynb b/demo/request_ip_headers/run.ipynb new file mode 100644 index 0000000000..5f39209171 --- /dev/null +++ b/demo/request_ip_headers/run.ipynb @@ -0,0 +1 @@ +{"cells": [{"cell_type": "markdown", "id": 302934307671667531413257853548643485645, "metadata": {}, "source": ["# Gradio Demo: request_ip_headers"]}, {"cell_type": "code", "execution_count": null, "id": 272996653310673477252411125948039410165, "metadata": {}, "outputs": [], "source": ["!pip install -q gradio "]}, {"cell_type": "code", "execution_count": null, "id": 288918539441861185822528903084949547379, "metadata": {}, "outputs": [], "source": ["import gradio as gr\n", "\n", "\n", "def predict(text, request: gr.Request):\n", " headers = request.headers\n", " host = request.client.host\n", " user_agent = request.headers[\"user-agent\"]\n", " return {\n", " \"ip\": host,\n", " \"user_agent\": user_agent,\n", " \"headers\": headers,\n", " }\n", "\n", "\n", "gr.Interface(predict, \"text\", \"json\").queue().launch()\n"]}], "metadata": {}, "nbformat": 4, "nbformat_minor": 5} \ No newline at end of file diff --git a/demo/request_ip_headers/run.py b/demo/request_ip_headers/run.py new file mode 100644 index 0000000000..fbd9fff25c --- /dev/null +++ b/demo/request_ip_headers/run.py @@ -0,0 +1,15 @@ +import gradio as gr + + +def predict(text, request: gr.Request): + headers = request.headers + host = request.client.host + user_agent = request.headers["user-agent"] + return { + "ip": host, + "user_agent": user_agent, + "headers": headers, + } + + +gr.Interface(predict, "text", "json").queue().launch() diff --git a/gradio/routes.py b/gradio/routes.py index c713d94c0d..2158b58ae2 100644 --- a/gradio/routes.py +++ b/gradio/routes.py @@ -635,8 +635,42 @@ class Obj: Credit: https://www.geeksforgeeks.org/convert-nested-python-dictionary-to-object/ """ - def __init__(self, dict1): - self.__dict__.update(dict1) + def __init__(self, dict_): + self.__dict__.update(dict_) + for key, value in dict_.items(): + if isinstance(value, (dict, list)): + value = Obj(value) + setattr(self, key, value) + + def __getitem__(self, item): + return self.__dict__[item] + + def __setitem__(self, item, value): + self.__dict__[item] = value + + def __iter__(self): + for key, value in self.__dict__.items(): + if isinstance(value, Obj): + yield (key, dict(value)) + else: + yield (key, value) + + def __contains__(self, item) -> bool: + if item in self.__dict__: + return True + for value in self.__dict__.values(): + if isinstance(value, Obj) and item in value: + return True + return False + + def keys(self): + return self.__dict__.keys() + + def values(self): + return self.__dict__.values() + + def items(self): + return self.__dict__.items() def __str__(self) -> str: return str(self.__dict__) diff --git a/test/test_routes.py b/test/test_routes.py index fe81cb836a..374a48fe3a 100644 --- a/test/test_routes.py +++ b/test/test_routes.py @@ -439,6 +439,27 @@ class TestPassingRequest: output = dict(response.json()) assert output["data"] == ["test"] + def test_request_get_headers(self): + def identity(name, request: gr.Request): + assert isinstance(request.headers["user-agent"], str) + assert isinstance(request.headers.items(), list) + assert isinstance(request.headers.keys(), list) + assert isinstance(request.headers.values(), list) + assert isinstance(dict(request.headers), dict) + user_agent = request.headers["user-agent"] + assert "testclient" in user_agent + return name + + app, _, _ = gr.Interface(identity, "textbox", "textbox").launch( + prevent_thread_lock=True, + ) + client = TestClient(app) + + response = client.post("/api/predict/", json={"data": ["test"]}) + assert response.status_code == 200 + output = dict(response.json()) + assert output["data"] == ["test"] + def test_request_includes_username_as_none_if_no_auth(self): def identity(name, request: gr.Request): assert request.username is None