diff --git a/.changeset/eight-candies-stick.md b/.changeset/eight-candies-stick.md new file mode 100644 index 0000000000000..52a5971d60303 --- /dev/null +++ b/.changeset/eight-candies-stick.md @@ -0,0 +1,5 @@ +--- +"gradio": patch +--- + +fix:Fix HTTPX package crash for some values of "article" parameter in the interface diff --git a/gradio/interface.py b/gradio/interface.py index 68490a3ae3abf..24162c980e3c4 100644 --- a/gradio/interface.py +++ b/gradio/interface.py @@ -134,7 +134,7 @@ def __init__( live: Whether the interface should automatically rerun if any of the inputs change. title: A title for the interface; if provided, appears above the input and output components in large font. Also used as the tab title when opened in a browser window. description: A description for the interface; if provided, appears above the input and output components and beneath the title in regular font. Accepts Markdown and HTML content. - article: An expanded article explaining the interface; if provided, appears below the input and output components in regular font. Accepts Markdown and HTML content. + article: An expanded article explaining the interface; if provided, appears below the input and output components in regular font. Accepts Markdown and HTML content. If it is an HTTP(S) link to a downloadable remote file, the content of this file is displayed. thumbnail: String path or url to image to use as display image when the web demo is shared on social media. theme: A Theme object or a string representing a theme. If a string, will look for a built-in theme with that name (e.g. "soft" or "default"), or will attempt to load a theme from the Hugging Face Hub (e.g. "gradio/monochrome"). If None, will use the Default theme. css: Custom css as a string or path to a css file. This css will be included in the demo webpage. @@ -295,7 +295,7 @@ def __init__( self.simple_description = utils.remove_html_tags(description) self.description = description if article is not None: - article = utils.readme_to_html(article) + article = utils.download_if_url(article) self.article = article self.thumbnail = thumbnail diff --git a/gradio/utils.py b/gradio/utils.py index 3418808fa0eed..f173476ec4eba 100644 --- a/gradio/utils.py +++ b/gradio/utils.py @@ -286,13 +286,24 @@ def is_zero_gpu_space() -> bool: return os.getenv("SPACES_ZERO_GPU") == "true" -def readme_to_html(article: str) -> str: +def download_if_url(article: str) -> str: + try: + result = urllib.parse.urlparse(article) + is_url = all([result.scheme, result.netloc, result.path]) + is_url = is_url and result.scheme in ["http", "https"] + except ValueError: + is_url = False + + if not is_url: + return article + try: response = httpx.get(article, timeout=3) if response.status_code == httpx.codes.OK: # pylint: disable=no-member article = response.text except (httpx.InvalidURL, httpx.RequestError): pass + return article diff --git a/test/test_utils.py b/test/test_utils.py index f5a708b6b9634..d6d191ef03b3b 100644 --- a/test/test_utils.py +++ b/test/test_utils.py @@ -27,7 +27,7 @@ is_in_or_equal, is_special_typed_parameter, kaggle_check, - readme_to_html, + download_if_url, sagemaker_check, sanitize_list_for_csv, sanitize_value_for_csv, @@ -54,13 +54,34 @@ def test_ipython_check_no_ipython(self, mock_get_ipython): mock_get_ipython.return_value = None assert ipython_check() is False - @patch("httpx.get") - def test_readme_to_html_doesnt_crash_on_connection_error(self, mock_get): - mock_get.side_effect = httpx.ConnectError("Connection error") - readme_to_html("placeholder") - - def test_readme_to_html_correct_parse(self): - readme_to_html("https://github.com/gradio-app/gradio/blob/master/README.md") + def test_download_if_url_doesnt_crash_on_connection_error(self): + in_article = "placeholder" + out_article = download_if_url(in_article) + assert out_article == in_article + + # non-printable characters are not allowed in URL address + in_article = "text\twith\rnon-printable\nASCII\x00characters" + out_article = download_if_url(in_article) + assert out_article == in_article + + # only files with HTTP(S) URL can be downloaded + in_article = "ftp://localhost/tmp/index.html" + out_article = download_if_url(in_article) + assert out_article == in_article + + in_article = "file:///C:/tmp/index.html" + out_article = download_if_url(in_article) + assert out_article == in_article + + # this address will raise ValueError during parsing + in_article = "https://[unmatched_bracket#?:@/index.html" + out_article = download_if_url(in_article) + assert out_article == in_article + + def test_download_if_url_correct_parse(self): + in_article = "https://github.com/gradio-app/gradio/blob/master/README.md" + out_article = download_if_url(in_article) + assert out_article != in_article def test_sagemaker_check_false(self): assert not sagemaker_check()