Spaces:
Build error
Build error
from typing import List | |
import numpy as np | |
import requests | |
import gradio as gr | |
import time | |
from huggingface_hub import ( | |
create_repo, | |
get_full_repo_name, | |
upload_file, | |
) | |
class SpaceBuilder: | |
error_message = None | |
url = None | |
def split_space_names(cls, names: str) -> List[str]: | |
""" | |
Splits and filters the given space_names. | |
:param names: space names | |
:return: Name List | |
""" | |
name_list = names.split("\n") | |
filtered_list = [] | |
for name in name_list: | |
if not (name == "" or name.isspace()): | |
filtered_list.append(f"spaces/{name}") | |
return filtered_list | |
def file_as_a_string(cls, name_list: List[str], title: str, description: str) -> str: | |
""" | |
Returns the file that is going to be created in the new space as string. | |
:param name_list: list of space names | |
:param title: title | |
:param description: description | |
:return: file as a string | |
""" | |
return ( | |
f"import gradio as gr" | |
f"\nname_list = {name_list}" | |
f"\ninterfaces = [gr.Interface.load(name) for name in name_list]" | |
f"\ngr.mix.Parallel(*interfaces, title=\"{title}\", description=\"{description}\").launch()" | |
) | |
def control_input_and_output_types( | |
cls, interface_list: List["gr.Interface"] | |
) -> bool: | |
""" | |
Controls whether if input and output types of the given interfaces are the same. | |
:param interface_list: list of interfaces | |
:return: True if all input and output types are the same | |
""" | |
first_input_types = [ | |
type(input) for input in interface_list[0].input_components | |
] | |
first_output_types = [ | |
type(output) for output in interface_list[0].output_components | |
] | |
for interface in interface_list: | |
interface_input_types = [ | |
type(input) for input in interface.input_components | |
] | |
if not np.all( | |
interface_input_types == first_input_types | |
): # Vectorize the comparison and don't use double for loop | |
cls.error_message = "Provided space input types are different" | |
return False | |
interface_output_types = [ | |
type(output) for output in interface.output_components | |
] | |
if not np.all(interface_output_types == first_output_types): | |
cls.error_message = "Provided space output types are different" | |
return False | |
return True | |
def check_space_name_availability(cls, hf_token: str, space_name: str) -> bool: | |
""" | |
Check whether if the space_name is currently used. | |
:param hf_token: hugging_face token | |
:param space_name: | |
:return: True if the space_name is available | |
""" | |
try: | |
repo_name = get_full_repo_name(model_id=space_name, token=hf_token) | |
except Exception as ex: | |
print(ex) | |
cls.error_message = "You have given an incorrect HuggingFace token" | |
return False | |
try: | |
url = f"https://huggingface.co/spaces/{repo_name}" | |
response = requests.get(url) | |
if response.status_code == 200: | |
cls.error_message = f"The {repo_name} is already used." | |
return False | |
else: | |
print(f"The space name {repo_name} is available") | |
return True | |
except Exception as ex: | |
print(ex) | |
cls.error_message = "Can not send a request to https://huggingface.co" | |
return False | |
def load_and_check_spaces(cls, names: str) -> bool: | |
""" | |
Loads given space inputs as interfaces and checks whether if they are loadable. | |
:param names: Input space names | |
:return: True if check is successful | |
""" | |
name_list = cls.split_space_names(names) | |
try: | |
# We could gather these interfaces in parallel if gradio was supporting async gathering. It will probably be possible after the migration to the FastAPI is completed. | |
interfaces = [gr.Interface.load(name) for name in name_list] | |
except Exception as ex: | |
print(ex) | |
cls.error_message = ( | |
f"One of the given space cannot be loaded to gradio, sorry for the inconvenience. " | |
f"\nPlease use different input space names!" | |
) | |
return False | |
if not cls.control_input_and_output_types(interfaces): | |
return False | |
else: | |
print("Loaded and checked input spaces, great it works!") | |
return True | |
def create_space(cls, input_space_names: str, target_space_name: str, hf_token: str, title: str, description: str) -> bool: | |
""" | |
Creates the target space with the given space names. | |
:param input_space_names: Input space name_list | |
:param target_space_name: Target space_name | |
:param hf_token: HuggingFace Write Token | |
:param title: Target Interface Title | |
:param description: Target Interface Description | |
:return: True if success | |
""" | |
name_list = cls.split_space_names(input_space_names) | |
try: | |
create_repo(name=target_space_name, token=hf_token, repo_type="space", space_sdk="gradio") | |
except Exception as ex: | |
print(ex) | |
cls.error_message = "Please provide a correct space name as Only regular characters and '-', '_', '.' accepted. '--' and '..' are forbidden. '-' and '.' cannot start or end the name." | |
return False | |
repo_name = get_full_repo_name(model_id=target_space_name, token=hf_token) | |
try: | |
file_string = cls.file_as_a_string(name_list, title, description) | |
temp_file = open("temp_file.txt", "w") | |
temp_file.write(file_string) | |
temp_file.close() | |
except Exception as ex: | |
print(ex) | |
cls.error_message = "An exception occurred during temporary file writing" | |
return False | |
# Sleep a little bit otherwise the interface might not build at the space after uploading a file | |
time.sleep(1) | |
try: | |
file_url = upload_file( | |
path_or_fileobj="temp_file.txt", | |
path_in_repo="app.py", | |
repo_id=repo_name, | |
repo_type="space", | |
token=hf_token, | |
) | |
cls.url = f"https://huggingface.co/spaces/{repo_name}" | |
return True | |
except Exception as ex: | |
print(ex) | |
cls.error_message = ( | |
"An exception occurred during writing app.py to the target space" | |
) | |
return False | |
def build_space( | |
space_names: str, hf_token: str, target_space_name: str, interface_title: str, interface_description: str | |
) -> str: | |
""" | |
Creates a space with given input spaces. | |
:param space_names: Multiple space names split with new lines | |
:param hf_token: HuggingFace token | |
:param target_space_name: Target Space Name | |
:param interface_title: Target Interface Title | |
:param interface_description: Target Interface Description | |
:return: | |
""" | |
if ( | |
space_names == "" or space_names.isspace() | |
or hf_token == "" or hf_token.isspace() | |
or target_space_name == "" or target_space_name.isspace() | |
or interface_title == "" or interface_title.isspace() | |
or interface_description == "" or interface_description.isspace() | |
): | |
return "Please fill all the inputs" | |
if not SpaceBuilder.check_space_name_availability(hf_token=hf_token, space_name=target_space_name): | |
return SpaceBuilder.error_message | |
if not SpaceBuilder.load_and_check_spaces(names=space_names): | |
return SpaceBuilder.error_message | |
if not SpaceBuilder.create_space(input_space_names=space_names, target_space_name=target_space_name, hf_token=hf_token, title=interface_title, description=interface_description): | |
return SpaceBuilder.error_message | |
return SpaceBuilder.url | |
if __name__ == "__main__": | |
iface = gr.Interface( | |
fn=SpaceBuilder.build_space, | |
inputs=[ | |
gr.inputs.Textbox( | |
lines=4, | |
placeholder=( | |
f"Drop space links at each line and I will create a new space comparing them. ie:" | |
f"\ndeepklarity/poster2plot" | |
f"\ndeepklarity/poster2plot" | |
), | |
), | |
gr.inputs.Textbox(lines=1, placeholder="HuggingFace Write Token"), | |
gr.inputs.Textbox(lines=1, placeholder="Name for the target space, ie. space-building-space"), | |
gr.inputs.Textbox(lines=1, placeholder="Title for the target space interface, ie. Title"), | |
gr.inputs.Textbox(lines=1, placeholder="Description for the target space interface, ie. Description"), | |
], | |
title="Space that builds another Space", | |
description="I can create another space which will compare the spaces you provide to me", | |
outputs="text", | |
) | |
iface.launch() | |