mgbam commited on
Commit
3e1c4b9
·
verified ·
1 Parent(s): 1dcead8

Update deploy.py

Browse files
Files changed (1) hide show
  1. deploy.py +178 -78
deploy.py CHANGED
@@ -1,98 +1,198 @@
 
 
 
 
 
 
 
 
1
  import gradio as gr
2
- from typing import Tuple, Optional
3
 
 
 
 
4
 
5
- def send_to_sandbox(code):
6
- wrapped_code=f"""
 
7
  <!DOCTYPE html>
8
  <html>
9
- <head>
10
- <meta charset="UTF-8">
11
- ...
12
- </body>
13
  </html>
14
  """
15
- encoded_html=base64.b64encode(wrapped_code.encode('utf-8')).decode('utf-8')
16
- data_uri=f"data:text/html;charset=utf-8;base64,{encoded_html}"
17
- return f'<iframe src="{data_uri}" width="100%" height="920px" sandbox="allow-scripts allow-same-origin allow-forms allow-popups allow-modals allow-presentation" allow="display-capture"></iframe>'
 
 
 
18
 
19
- def demo_card_click(e: gr.EventData):
20
- ...
21
- return DEMO_LIST[index]['description']
 
 
 
 
 
 
 
 
 
 
 
22
 
23
- def wrap_html_in_gradio_app(html_code):
24
- safe_html=html_code.replace('"""',r'\"\"\"')
 
 
 
 
 
25
  return (
26
- 'import gradio as gr\n\n'
27
- 'def show_html():\n'
28
- f' return """{safe_html}"""\n\n'
29
- 'demo = gr.Interface(fn=show_html, inputs=None, outputs=gr.HTML())\n\n'
30
- 'if __name__ == "__main__":\n'
31
- ' demo.launch()\n'
32
  )
33
 
34
- def deploy_to_spaces(code):
35
- if not code.strip(): return
36
- app_py=wrap_html_in_gradio_app(code.strip())
37
- ...
38
- webbrowser.open_new_tab(full_url)
 
 
 
 
 
 
 
 
 
 
 
39
 
40
- def wrap_html_in_static_app(html_code):
 
41
  return html_code
42
 
43
- def deploy_to_spaces_static(code):
44
- if not code.strip(): return
45
- app_html=wrap_html_in_static_app(code.strip())
46
- ...
47
- webbrowser.open_new_tab(full_url)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
48
 
49
- def check_hf_space_url(url: str) -> Tuple[bool, Optional[str], Optional[str]]:
 
50
  import re
51
- url_pattern=re.compile(
52
- r'^(https?://)?(huggingface\.co|hf\.co)/spaces/([\w-]+)/([\w-]+)$',
53
- re.IGNORECASE
54
- )
55
- match=url_pattern.match(url.strip())
56
- if match:
57
- return True, match.group(3), match.group(4)
58
- return False, None, None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
59
 
60
- def fetch_hf_space_content(username: str, project_name: str) -> str:
 
 
 
 
61
  try:
62
- api=HfApi()
63
- space_info=api.space_info(f"{username}/{project_name}")
64
- sdk=space_info.sdk
65
- ...
66
- return f"""IMPORTED PROJECT FROM HUGGING FACE SPACE
67
- ==============================================
68
- Space: {username}/{project_name}
69
- SDK: {sdk}
70
- Main File: {main_file}
71
- {file_content}"""
72
  except Exception as e:
73
- return f"Error fetching space content: {str(e)}"
74
-
75
- def load_project_from_url(url: str) -> Tuple[str, str]:
76
- is_valid,username,project_name=check_hf_space_url(url)
77
- if not is_valid:
78
- return "Error: Please enter a valid Hugging Face Spaces URL.\n\nExpected format: https://huggingface.co/spaces/username/project",""
79
- content=fetch_hf_space_content(username,project_name)
80
- if content.startswith("Error:"):
81
- return content,""
82
- lines=content.split('\n')
83
- code_start=0
84
- for i,line in enumerate(lines):
85
- if line.strip() and not line.startswith(('=','IMPORTED','Space:','SDK:','Main File:')):
86
- code_start=i
87
- break
88
- code_content='\n'.join(lines[code_start:])
89
- return f" Successfully imported project from {username}/{project_name}",code_content
90
-
91
- def deploy_to_user_space(code, space_name, sdk_name, profile=None, token=None):
92
- import shutil
93
- if not code.strip():
94
- return gr.update(value="No code to deploy.", visible=True)
95
- if profile is None or token is None:
96
- return gr.update(value="Please log in with your Hugging Face account to deploy to your own Space. Otherwise, use the default deploy (opens in new tab).", visible=True)
97
- ...
98
- return gr.update(value=f"✅ {action_text}! [Open your Space here]({space_url})", visible=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # deploy.py
2
+
3
+ import os
4
+ import base64
5
+ import webbrowser
6
+ import urllib.parse
7
+ import tempfile
8
+
9
  import gradio as gr
10
+ from huggingface_hub import HfApi, duplicate_space
11
 
12
+ # ------------------------------------------------------------------
13
+ # Utilities for live‐preview sandbox in the Gradio UI
14
+ # ------------------------------------------------------------------
15
 
16
+ def send_to_sandbox(code: str) -> str:
17
+ """Wrap HTML code in a sandboxed iframe via a data URI."""
18
+ wrapped = f"""
19
  <!DOCTYPE html>
20
  <html>
21
+ <head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"></head>
22
+ <body>{code}</body>
 
 
23
  </html>
24
  """
25
+ b64 = base64.b64encode(wrapped.encode("utf-8")).decode("utf-8")
26
+ return (
27
+ f'<iframe src="data:text/html;base64,{b64}" '
28
+ 'width="100%" height="920px" sandbox="allow-scripts allow-same-origin '
29
+ 'allow-forms allow-popups allow-modals allow-presentation" allow="display-capture"></iframe>'
30
+ )
31
 
32
+ def demo_card_click(e: gr.EventData) -> str:
33
+ """Return the description of a demo card when clicked."""
34
+ try:
35
+ idx = (
36
+ e._data.get("index")
37
+ or e._data.get("component", {}).get("index")
38
+ or e._data.get("target", {}).get("index")
39
+ )
40
+ except Exception:
41
+ idx = 0
42
+ from constants import DEMO_LIST
43
+ if not (0 <= idx < len(DEMO_LIST)):
44
+ idx = 0
45
+ return DEMO_LIST[idx]["description"]
46
 
47
+ # ------------------------------------------------------------------
48
+ # Functions for importing existing Hugging Face Spaces projects
49
+ # ------------------------------------------------------------------
50
+
51
+ def wrap_html_in_gradio_app(html_code: str) -> str:
52
+ """Generate a minimal Gradio app.py that renders given HTML."""
53
+ safe = html_code.replace('"""', r'\"\"\"')
54
  return (
55
+ "import gradio as gr\n\n"
56
+ "def show_html():\n"
57
+ f' return """{safe}"""\n\n'
58
+ "demo = gr.Interface(fn=show_html, inputs=None, outputs=gr.HTML())\n\n"
59
+ "if __name__ == '__main__':\n"
60
+ " demo.launch()\n"
61
  )
62
 
63
+ def deploy_to_spaces(code: str) -> None:
64
+ """Open a new tab to create a Hugging Face Space with a Gradio app."""
65
+ if not code.strip():
66
+ return
67
+ app_py = wrap_html_in_gradio_app(code)
68
+ params = urllib.parse.urlencode({
69
+ "name": "new-space",
70
+ "sdk": "gradio"
71
+ })
72
+ files = {
73
+ "files[0][path]": "app.py",
74
+ "files[0][content]": app_py
75
+ }
76
+ files_params = urllib.parse.urlencode(files)
77
+ url = f"https://huggingface.co/new-space?{params}&{files_params}"
78
+ webbrowser.open_new_tab(url)
79
 
80
+ def wrap_html_in_static_app(html_code: str) -> str:
81
+ """Return raw HTML for a static Hugging Face Space."""
82
  return html_code
83
 
84
+ def deploy_to_spaces_static(code: str) -> None:
85
+ """Open a new tab to create a static HTML Space."""
86
+ if not code.strip():
87
+ return
88
+ html = wrap_html_in_static_app(code)
89
+ params = urllib.parse.urlencode({
90
+ "name": "new-space",
91
+ "sdk": "static"
92
+ })
93
+ files = {
94
+ "files[0][path]": "index.html",
95
+ "files[0][content]": html
96
+ }
97
+ files_params = urllib.parse.urlencode(files)
98
+ url = f"https://huggingface.co/new-space?{params}&{files_params}"
99
+ webbrowser.open_new_tab(url)
100
+
101
+ # ------------------------------------------------------------------
102
+ # Functions for loading and updating user Spaces
103
+ # ------------------------------------------------------------------
104
 
105
+ def check_hf_space_url(url: str):
106
+ """Validate a HF Spaces URL and extract username/project."""
107
  import re
108
+ pattern = re.compile(r'^(?:https?://)?(?:huggingface\.co|hf\.co)/spaces/([\w-]+)/([\w-]+)$', re.IGNORECASE)
109
+ m = pattern.match(url.strip())
110
+ if not m:
111
+ return False, None, None
112
+ return True, m.group(1), m.group(2)
113
+
114
+ def fetch_hf_space_content(username: str, project: str) -> str:
115
+ """Download the main file of a Space and return its content."""
116
+ api = HfApi()
117
+ info = api.space_info(f"{username}/{project}")
118
+ sdk = info.sdk
119
+ # Choose main file based on SDK
120
+ if sdk == "static":
121
+ main_file = "index.html"
122
+ elif sdk == "gradio":
123
+ main_file = "app.py"
124
+ else:
125
+ # Fallback: try common filenames
126
+ for fname in ["app.py", "index.html", "streamlit_app.py", "main.py"]:
127
+ try:
128
+ _ = api.hf_hub_download(repo_id=f"{username}/{project}", filename=fname, repo_type="space")
129
+ main_file = fname
130
+ break
131
+ except:
132
+ continue
133
+ path = api.hf_hub_download(repo_id=f"{username}/{project}", filename=main_file, repo_type="space")
134
+ with open(path, "r", encoding="utf-8") as f:
135
+ return f.read()
136
 
137
+ def load_project_from_url(url: str):
138
+ """Return (status_message, code_content) for importing a Space URL."""
139
+ valid, user, proj = check_hf_space_url(url)
140
+ if not valid:
141
+ return "Error: Invalid Hugging Face Space URL.", ""
142
  try:
143
+ content = fetch_hf_space_content(user, proj)
144
+ return f"✅ Imported {user}/{proj}", content
 
 
 
 
 
 
 
 
145
  except Exception as e:
146
+ return f"Error fetching project: {e}", ""
147
+
148
+ def deploy_to_user_space(code, space_name, sdk_choice, profile: gr.OAuthProfile | None = None, token: gr.OAuthToken | None = None):
149
+ """Create or update a user's own HF Space with proper authentication."""
150
+ if not profile or not token or not token.token or token.token.startswith("hf_"):
151
+ return gr.update(value="Please log in with a valid Hugging Face write token.", visible=True)
152
+
153
+ api = HfApi(token=token.token)
154
+ is_update = "/" in space_name.strip()
155
+ if is_update:
156
+ repo_id = space_name.strip()
157
+ else:
158
+ repo_id = f"{profile.username}/{space_name.strip()}"
159
+ sdk_map = {
160
+ "Gradio (Python)": "gradio",
161
+ "Streamlit (Python)": "docker",
162
+ "Static (HTML)": "static",
163
+ "Transformers.js": "static"
164
+ }
165
+ sdk = sdk_map.get(sdk_choice, "gradio")
166
+
167
+ # Create or update repo
168
+ if not is_update and sdk != "docker":
169
+ api.create_repo(repo_id=repo_id, repo_type="space", space_sdk=sdk, exist_ok=True)
170
+
171
+ # Duplicate and upload logic for docker (Streamlit) or transformers.js
172
+ if sdk == "docker":
173
+ dup = duplicate_space(from_id="streamlit/streamlit-template-space", to_id=repo_id, token=token.token, exist_ok=True)
174
+ with tempfile.NamedTemporaryFile("w", suffix=".py", delete=False) as f:
175
+ f.write(code)
176
+ path = f.name
177
+ api.upload_file(path_or_fileobj=path, path_in_repo="src/streamlit_app.py", repo_id=repo_id, repo_type="space")
178
+ os.unlink(path)
179
+ elif sdk_choice == "Transformers.js":
180
+ dup = duplicate_space(from_id="static-templates/transformers.js", to_id=repo_id, token=token.token, exist_ok=True)
181
+ from utils import parse_transformers_js_output # ensure we have that helper
182
+ files = parse_transformers_js_output(code)
183
+ for name, content in files.items():
184
+ with tempfile.NamedTemporaryFile("w", suffix=f".{name.split('.')[-1]}", delete=False) as f:
185
+ f.write(content)
186
+ path = f.name
187
+ api.upload_file(path_or_fileobj=path, path_in_repo=name, repo_id=repo_id, repo_type="space")
188
+ os.unlink(path)
189
+ else:
190
+ # Static or Gradio: upload single file
191
+ filename = "index.html" if sdk == "static" else "app.py"
192
+ with tempfile.NamedTemporaryFile("w", suffix=f".{filename.split('.')[-1]}", delete=False) as f:
193
+ f.write(code)
194
+ path = f.name
195
+ api.upload_file(path_or_fileobj=path, path_in_repo=filename, repo_id=repo_id, repo_type="space")
196
+ os.unlink(path)
197
+
198
+ return gr.update(value=f"✅ Deployed to https://huggingface.co/spaces/{repo_id}", visible=True)