gradio/test/test_theme_sharing.py

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

422 lines
13 KiB
Python
Raw Normal View History

import tempfile
from unittest.mock import patch
import huggingface_hub
import pytest
from huggingface_hub.hf_api import SpaceInfo
import gradio as gr
from gradio.themes.utils import ThemeAsset, get_matching_version, get_theme_assets
versions = [
"0.1.0",
"0.1.1",
"0.1.2",
"0.1.3",
"0.4.4",
"0.5.0",
"0.7.0",
"0.9.2",
"0.9.3",
"0.9.4",
"0.9.5",
"0.9.6",
"0.9.7",
"0.9.8",
"2.2.0",
"2.2.1",
"2.2.10",
"2.2.11",
"2.2.12",
"2.2.13",
"2.2.14",
"2.2.15",
"2.2.2",
"2.2.3",
"2.2.4",
"2.2.5",
"2.2.6",
"2.2.7",
"2.2.8",
"3.0.1",
"3.0.10",
"3.0.11",
"3.0.12",
"3.0.13",
"3.0.14",
"3.0.15",
"3.0.16",
"3.0.17",
"3.0.18",
"3.0.19",
"3.0.2",
"3.0.20",
"3.0.20-dev0",
"3.0.21",
"3.0.22",
"3.0.23",
"3.0.23-dev1",
"3.0.24",
"3.0.25",
"3.0.26",
"3.0.3",
"3.0.4",
"3.0.5",
"3.0.6",
"3.0.7",
"3.0.8",
"3.0.9",
"3.1.0",
"3.1.1",
"3.1.2",
"3.1.3",
"3.1.4",
"3.1.5",
"3.1.6",
"3.1.7",
"3.10.0",
"3.10.1",
"3.11.0",
"3.12.0",
"3.13.0",
"3.13.1",
"3.13.2",
"3.14.0",
"3.15.0",
"3.16.0",
"3.16.1",
"3.16.2",
"3.17.0",
"3.17.1",
"3.18.0",
"3.18.1",
"3.18.1-dev0",
"3.18.2-dev0",
"3.18.2",
"3.19.0",
"3.19.1",
"3.20.0",
"3.20.1",
"3.3.1",
"3.4.1",
"3.8.1",
"3.8.2",
"3.9.1",
]
assets = [ThemeAsset(f"theme_schema@{version}") for version in versions]
dracula_gray = gr.themes.colors.Color(
*(
["#6272a4", "#7280ad", "#818eb6", "#919cbf", "#a1aac8"][::-1]
+ ["#586794", "#4e5b83", "#455073", "#3b4462", "#313952", "#272e42"]
)
)
dracula_pink = gr.themes.Color(
*(
[
"#ffd7ee",
"#ff79c6",
"#ff86cc",
"#ff94d1",
"#ffa1d7",
"#ffafdd",
"#ffbce3",
"#ffc9e8",
][::-1]
+ ["#e66db2", "#cc619e", "#b3558b"]
)
)
dracula_gray = gr.themes.colors.Color(
*(
["#6272a4", "#7280ad", "#818eb6", "#919cbf", "#a1aac8"][::-1]
+ ["#586794", "#4e5b83", "#455073", "#3b4462", "#313952", "#272e42"]
)
)
dracula = gr.themes.Base(
primary_hue=gr.themes.colors.pink,
neutral_hue=dracula_gray,
font=gr.themes.GoogleFont("Poppins"),
).set(
body_background_fill=dracula_gray.c500,
color_accent_soft=dracula_gray.c100,
error_border_color="#fecaca",
error_background_fill="#fee2e2",
error_icon_color="#b91c1c",
error_icon_color_dark="#ef4444",
error_text_color="#ef4444",
error_text_color_dark="#ef4444",
background_fill_primary=dracula_gray.c500,
background_fill_secondary=dracula_gray.c500,
block_background_fill=dracula_gray.c300,
body_text_color="#f8f8f2",
body_text_color_dark="#f8f8f2",
body_text_color_subdued="#f8f8f2",
block_label_text_color="#f8f8f2",
block_label_text_color_dark="#f8f8f2",
table_even_background_fill=dracula_gray.c300,
border_color_accent=dracula_gray.c200,
block_info_text_color="#f8f8f2",
block_info_text_color_dark="#f8f8f2",
block_title_text_color="#f8f8f2",
block_title_text_color_dark="#f8f8f2",
checkbox_background_color_selected_dark="#ff79c6",
checkbox_background_color_selected="#ff79c6",
button_primary_background_fill_dark="#ff79c6",
button_primary_background_fill=dracula_pink.c300,
button_secondary_text_color="#f8f8f2",
slider_color_dark="#ff79c6",
slider_color=dracula_pink.c300,
panel_background_fill="#31395294",
block_background_fill_dark="#31395294",
)
dracula.name = "gradio/dracula_test"
class TestSemverMatch:
def test_simple_equality(self):
assert get_matching_version(assets, "3.10.0") == ThemeAsset(
"theme_schema@3.10.0"
)
def test_empty_expression_returns_latest(self):
assert get_matching_version(assets, None) == ThemeAsset("theme_schema@3.20.1")
def test_range(self):
assert get_matching_version(assets, ">=3.10.0,<3.15") == ThemeAsset(
"theme_schema@3.14.0"
)
def test_wildcard(self):
assert get_matching_version(assets, "2.2.*") == ThemeAsset(
"theme_schema@2.2.15"
)
def test_does_not_exist(self):
assert get_matching_version(assets, ">4.0.0") is None
def test_compatible_release_specifier(self):
assert get_matching_version(assets, "~=0.0") == ThemeAsset("theme_schema@0.9.8")
def test_breaks_ties_against_prerelease(self):
assert get_matching_version(assets, ">=3.18,<3.19") == ThemeAsset(
"theme_schema@3.18.2"
)
class TestGetThemeAssets:
def test_get_theme_assets(self):
space_info = huggingface_hub.hf_api.SpaceInfo(
id="freddyaboulton/dracula",
siblings=[
{
"blob_id": None,
"lfs": None,
"rfilename": "themes/theme_schema@0.1.0.json",
"size": None,
},
{
"blob_id": None,
"lfs": None,
"rfilename": "themes/theme_schema@0.1.1.json",
"size": None,
},
{
"blob_id": None,
"lfs": None,
"rfilename": "themes/theme_schema@0.2.5.json",
"size": None,
},
{
"blob_id": None,
"lfs": None,
"rfilename": "themes/theme_schema@1.5.9.json",
"size": None,
},
],
tags=["gradio-theme", "gradio"],
private=False,
likes=0,
)
assert get_theme_assets(space_info) == [
ThemeAsset("themes/theme_schema@0.1.0.json"),
ThemeAsset("themes/theme_schema@0.1.1.json"),
ThemeAsset("themes/theme_schema@0.2.5.json"),
ThemeAsset("themes/theme_schema@1.5.9.json"),
]
assert gr.Theme._theme_version_exists(space_info, "0.1.1")
assert not gr.Theme._theme_version_exists(space_info, "2.0.0")
@pytest.mark.flaky
def test_load_space_from_hub_works(self):
theme = gr.Theme.from_hub("gradio/seafoam")
assert isinstance(theme, gr.Theme)
class TestBuiltInThemes:
@pytest.mark.parametrize(
"theme, name",
[
(gr.themes.Base(), "base"),
(gr.themes.Glass(), "glass"),
(gr.themes.Monochrome(), "monochrome"),
(gr.themes.Soft(), "soft"),
(gr.themes.Default(), "default"),
],
)
def test_theme_name(self, theme, name):
assert theme.name == name
class TestThemeUploadDownload:
@patch("gradio.themes.base.get_theme_assets", return_value=assets)
def test_get_next_version(self, mock):
next_version = gr.themes.Base._get_next_version(
SpaceInfo(id="gradio/dracula_test", private=False, likes=0, tags=[])
)
assert next_version == "3.20.2"
## Commenting out until after 4.0 Spaces are up
# @pytest.mark.flaky
# def test_theme_download(self):
# assert (
# gr.themes.Base.from_hub("gradio/dracula_test@0.0.2").to_dict()
# == dracula.to_dict()
# )
# with gr.Blocks(theme="gradio/dracula_test@0.0.2") as demo:
# pass
# assert demo.theme.to_dict() == dracula.to_dict()
# assert demo.theme.name == "gradio/dracula_test"
@pytest.mark.flaky
def test_theme_download_raises_error_if_theme_does_not_exist(self):
with pytest.raises(
ValueError, match="The space freddyaboulton/nonexistent does not exist"
):
gr.themes.Base.from_hub("freddyaboulton/nonexistent").to_dict()
@patch("gradio.themes.base.huggingface_hub")
@patch("gradio.themes.base.Base._theme_version_exists", return_value=True)
def test_theme_upload_fails_if_duplicate_version(self, mock_1, mock_2):
with pytest.raises(ValueError, match="already has a theme with version 0.2.1"):
dracula.push_to_hub("dracula_revamped", version="0.2.1", hf_token="foo")
@patch("gradio.themes.base.huggingface_hub")
@patch("gradio.themes.base.huggingface_hub.HfApi")
def test_upload_fails_if_not_valid_semver(self, mock_1, mock_2):
with pytest.raises(ValueError, match="Invalid version string: '3.0'"):
dracula.push_to_hub("dracula_revamped", version="3.0", hf_token="s")
def test_dump_and_load(self):
with tempfile.NamedTemporaryFile(suffix=".json", delete=False) as path:
dracula.dump(path.name)
assert gr.themes.Base.load(path.name).to_dict() == dracula.to_dict()
@patch("gradio.themes.base.Base._get_next_version", return_value="0.1.3")
@patch("gradio.themes.base.Base._theme_version_exists", return_value=False)
@patch("gradio.themes.base.huggingface_hub")
def test_version_and_token_optional(self, mock_1, mock_2, mock_3):
mock_1.whoami.return_value = {"name": "freddyaboulton"}
gr.themes.Monochrome().push_to_hub(repo_name="my_monochrome")
repo_call_args = mock_1.HfApi().create_commit.call_args_list[0][1]
assert repo_call_args["repo_id"] == "freddyaboulton/my_monochrome"
assert any(
o.path_in_repo == "themes/theme_schema@0.1.3.json"
for o in repo_call_args["operations"]
)
mock_1.whoami.assert_called_with()
@patch("gradio.themes.base.huggingface_hub")
def test_first_upload_no_version(self, mock_1):
mock_1.whoami.return_value = {"name": "freddyaboulton"}
mock_1.HfApi().space_info.side_effect = huggingface_hub.hf_api.HTTPError("Foo")
gr.themes.Monochrome().push_to_hub(repo_name="does_not_exist")
repo_call_args = mock_1.HfApi().create_commit.call_args_list[0][1]
assert repo_call_args["repo_id"] == "freddyaboulton/does_not_exist"
assert any(
o.path_in_repo == "themes/theme_schema@0.0.1.json"
for o in repo_call_args["operations"]
)
mock_1.whoami.assert_called_with()
@patch("gradio.themes.base.Base._get_next_version", return_value="0.1.3")
@patch("gradio.themes.base.Base._theme_version_exists", return_value=False)
@patch("gradio.themes.base.huggingface_hub")
def test_can_pass_version_and_theme(self, mock_1, mock_2, mock_3):
mock_1.whoami.return_value = {"name": "freddyaboulton"}
gr.themes.Monochrome().push_to_hub(
repo_name="my_monochrome", version="0.1.5", hf_token="foo"
)
repo_call_args = mock_1.HfApi().create_commit.call_args_list[0][1]
assert repo_call_args["repo_id"] == "freddyaboulton/my_monochrome"
assert any(
o.path_in_repo == "themes/theme_schema@0.1.5.json"
for o in repo_call_args["operations"]
)
mock_1.whoami.assert_called_with(token="foo")
@patch("gradio.themes.base.huggingface_hub")
def test_raise_error_if_no_token_and_not_logged_in(self, mock_1):
mock_1.whoami.side_effect = OSError("not logged in")
with pytest.raises(
ValueError,
match="In order to push to hub, log in via `huggingface-cli login`",
):
gr.themes.Monochrome().push_to_hub(
repo_name="my_monochrome", version="0.1.5"
)
@patch("gradio.themes.base.Base._get_next_version", return_value="0.1.3")
@patch("gradio.themes.base.Base._theme_version_exists", return_value=False)
@patch("gradio.themes.base.huggingface_hub")
def test_can_upload_to_org(self, mock_1, mock_2, mock_3):
mock_1.whoami.return_value = {"name": "freddyaboulton"}
gr.themes.Monochrome().push_to_hub(
repo_name="my_monochrome", version="0.1.9", org_name="gradio"
)
repo_call_args = mock_1.HfApi().create_commit.call_args_list[0][1]
assert repo_call_args["repo_id"] == "gradio/my_monochrome"
assert any(
o.path_in_repo == "themes/theme_schema@0.1.9.json"
for o in repo_call_args["operations"]
)
mock_1.whoami.assert_called_with()
@patch("gradio.themes.base.Base._get_next_version", return_value="0.1.3")
@patch("gradio.themes.base.Base._theme_version_exists", return_value=False)
@patch("gradio.themes.base.huggingface_hub")
def test_can_make_private(self, mock_1, mock_2, mock_3):
mock_1.whoami.return_value = {"name": "freddyaboulton"}
gr.themes.Monochrome().push_to_hub(
repo_name="my_monochrome", version="0.1.9", org_name="gradio", private=True
)
mock_1.create_repo.assert_called_with(
"gradio/my_monochrome",
repo_type="space",
space_sdk="gradio",
token=None,
exist_ok=True,
private=True,
)
def test_theme_builder_launches():
gr.themes.builder(prevent_thread_lock=True)
gr.close_all()