Spaces:
Sleeping
Sleeping
import gradio as gr | |
import os | |
import re | |
import tempfile | |
import shutil | |
import git | |
from huggingface_hub import ( | |
create_repo, | |
upload_folder, | |
list_repo_files, | |
delete_file, | |
Repository, | |
whoami, | |
) | |
from huggingface_hub.utils import HfHubHTTPError | |
import logging | |
from pathlib import Path | |
# Configure logging | |
logging.basicConfig(level=logging.INFO) | |
logger = logging.getLogger(__name__) | |
# Function to parse markdown input | |
def parse_markdown(markdown_input): | |
"""Parse markdown input to extract space details and file structure.""" | |
space_info = {"repo_name": "", "owner": "", "sdk": "gradio", "files": []} | |
current_file = None | |
file_content = [] | |
in_file_content = False | |
lines = markdown_input.strip().split("\n") | |
for line in lines: | |
line = line.strip() | |
# Extract space name | |
if line.startswith("# Space:"): | |
space_info["repo_name"] = line.replace("# Space:", "").strip() | |
if "/" in space_info["repo_name"]: | |
space_info["owner"], space_info["repo_name"] = space_info["repo_name"].split("/", 1) | |
# Detect file structure section | |
elif line.startswith("## File Structure"): | |
continue | |
# Detect file in structure | |
elif line.startswith("๐") or line.startswith("๐"): | |
if current_file and file_content: | |
space_info["files"].append({"path": current_file, "content": "\n".join(file_content)}) | |
file_content = [] | |
current_file = line[2:].strip() | |
in_file_content = False | |
# Detect file content section | |
elif line.startswith("### File:"): | |
if current_file and file_content: | |
space_info["files"].append({"path": current_file, "content": "\n".join(file_content)}) | |
file_content = [] | |
current_file = line.replace("### File:", "").strip() | |
in_file_content = True | |
# Handle file content | |
elif in_file_content and line.startswith("```"): | |
if file_content: | |
space_info["files"].append({"path": current_file, "content": "\n".join(file_content)}) | |
file_content = [] | |
in_file_content = False | |
else: | |
in_file_content = True | |
elif in_file_content: | |
file_content.append(line) | |
# Append the last file | |
if current_file and file_content: | |
space_info["files"].append({"path": current_file, "content": "\n".join(file_content)}) | |
return space_info | |
# Function to create and populate a Space | |
def create_space(api_token, space_name, owner, sdk, markdown_input): | |
"""Create a Hugging Face Space and populate it with files from markdown input.""" | |
try: | |
# Authenticate with Hugging Face Hub | |
if not api_token: | |
return "Error: Please provide a valid Hugging Face API token." | |
# Get user info | |
user_info = whoami(token=api_token) | |
if not owner: | |
owner = user_info["name"] | |
repo_id = f"{owner}/{space_name}" | |
# Create temporary directory | |
with tempfile.TemporaryDirectory() as temp_dir: | |
repo_path = os.path.join(temp_dir, space_name) | |
os.makedirs(repo_path, exist_ok=True) | |
# Parse markdown input | |
space_info = parse_markdown(markdown_input) | |
if space_info["repo_name"] != f"{owner}/{space_name}": | |
return "Error: Space name in markdown does not match provided owner/space_name." | |
# Write files to temporary directory | |
for file_info in space_info["files"]: | |
file_path = os.path.join(repo_path, file_info["path"]) | |
os.makedirs(os.path.dirname(file_path), exist_ok=True) | |
with open(file_path, "w", encoding="utf-8") as f: | |
f.write(file_info["content"]) | |
logger.info(f"Wrote file: {file_path}") | |
# Create repository on Hugging Face | |
try: | |
create_repo( | |
repo_id=repo_id, | |
token=api_token, | |
repo_type="space", | |
space_sdk=sdk, | |
private=False, | |
) | |
logger.info(f"Created Space: {repo_id}") | |
except HfHubHTTPError as e: | |
if "already exists" in str(e): | |
logger.info(f"Space {repo_id} already exists, proceeding to update.") | |
else: | |
return f"Error creating Space: {str(e)}" | |
# Initialize Git repository | |
repo = git.Repo.init(repo_path) | |
repo.git.add(all=True) | |
repo.index.commit("Initial commit from Gradio app") | |
# Push to Hugging Face Space | |
upload_folder( | |
repo_id=repo_id, | |
folder_path=repo_path, | |
path_in_repo=".", | |
token=api_token, | |
commit_message="Initial Space setup", | |
) | |
return f"Successfully created Space: https://huggingface.co/spaces/{repo_id}" | |
except Exception as e: | |
logger.error(f"Error: {str(e)}") | |
return f"Error: {str(e)}" | |
# Function to view Space files | |
def view_space_files(api_token, space_name, owner): | |
"""List files in a Hugging Face Space.""" | |
try: | |
if not api_token: | |
return "Error: Please provide a valid Hugging Face API token." | |
repo_id = f"{owner}/{space_name}" if owner else space_name | |
files = list_repo_files(repo_id=repo_id, token=api_token, repo_type="space") | |
return "\n".join(files) or "No files found in the Space." | |
except Exception as e: | |
return f"Error: {str(e)}" | |
# Function to update a Space file | |
def update_space_file(api_token, space_name, owner, file_path, file_content, commit_message): | |
"""Update a file in a Hugging Face Space with a commit.""" | |
try: | |
if not api_token: | |
return "Error: Please provide a valid Hugging Face API token." | |
repo_id = f"{owner}/{space_name}" if owner else space_name | |
# Create temporary directory for cloning | |
with tempfile.TemporaryDirectory() as temp_dir: | |
repo_path = os.path.join(temp_dir, space_name) | |
repo = Repository( | |
local_dir=repo_path, | |
clone_from=f"https://huggingface.co/spaces/{repo_id}", | |
repo_type="space", | |
use_auth_token=api_token, | |
) | |
# Write updated file | |
full_path = os.path.join(repo_path, file_path) | |
os.makedirs(os.path.dirname(full_path), exist_ok=True) | |
with open(full_path, "w", encoding="utf-8") as f: | |
f.write(file_content) | |
# Commit and push changes | |
repo.git_add(file_path) | |
repo.git_commit(commit_message or f"Update {file_path}") | |
repo.git_push() | |
return f"Successfully updated {file_path} in Space: https://huggingface.co/spaces/{repo_id}" | |
except Exception as e: | |
return f"Error: {str(e)}" | |
# Gradio interface | |
def main(): | |
with gr.Blocks(theme=gr.themes.Soft()) as demo: | |
gr.Markdown("# Hugging Face Space Builder") | |
gr.Markdown("Create, view, and update Hugging Face Spaces with a custom file structure.") | |
# Authentication | |
with gr.Group(): | |
gr.Markdown("## Authentication") | |
api_token = gr.Textbox( | |
label="Hugging Face API Token", | |
type="password", | |
placeholder="Enter your Hugging Face API token", | |
) | |
# Create Space | |
with gr.Group(): | |
gr.Markdown("## Create a New Space") | |
space_name = gr.Textbox(label="Space Name", placeholder="my-space") | |
owner = gr.Textbox( | |
label="Owner (optional)", placeholder="Leave blank for current user" | |
) | |
sdk = gr.Dropdown( | |
label="SDK", choices=["gradio", "streamlit", "docker", "static"], value="gradio" | |
) | |
markdown_input = gr.Textbox( | |
label="Markdown File Structure", | |
placeholder="Paste markdown with file structure and contents", | |
lines=10, | |
) | |
create_btn = gr.Button("Create Space") | |
create_output = gr.Textbox(label="Result") | |
# View Space Files | |
with gr.Group(): | |
gr.Markdown("## View Space Files") | |
view_space_name = gr.Textbox(label="Space Name", placeholder="my-space") | |
view_owner = gr.Textbox( | |
label="Owner (optional)", placeholder="Leave blank for current user" | |
) | |
view_btn = gr.Button("List Files") | |
view_output = gr.Textbox(label="Files in Space") | |
# Update Space File | |
with gr.Group(): | |
gr.Markdown("## Update Space File") | |
update_space_name = gr.Textbox(label="Space Name", placeholder="my-space") | |
update_owner = gr.Textbox( | |
label="Owner (optional)", placeholder="Leave blank for current user" | |
) | |
file_path = gr.Textbox(label="File Path", placeholder="path/to/file.py") | |
file_content = gr.Textbox( | |
label="File Content", placeholder="Enter new file content", lines=10 | |
) | |
commit_message = gr.Textbox( | |
label="Commit Message", placeholder="Update file content" | |
) | |
update_btn = gr.Button("Update File") | |
update_output = gr.Textbox(label="Result") | |
# Event handlers | |
create_btn.click( | |
fn=create_space, | |
inputs=[api_token, space_name, owner, sdk, markdown_input], | |
outputs=create_output, | |
) | |
view_btn.click( | |
fn=view_space_files, | |
inputs=[api_token, view_space_name, view_owner], | |
outputs=view_output, | |
) | |
update_btn.click( | |
fn=update_space_file, | |
inputs=[ | |
api_token, | |
update_space_name, | |
update_owner, | |
file_path, | |
file_content, | |
commit_message, | |
], | |
outputs=update_output, | |
) | |
return demo | |
if __name__ == "__main__": | |
demo = main() | |
demo.launch() |