build-space / app.py
broadfield-dev's picture
Update app.py
ec65380 verified
raw
history blame
10.3 kB
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,
)
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 Exception 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()