|
import gradio as gr |
|
import os |
|
import subprocess |
|
import sys |
|
import shutil |
|
import uuid |
|
import platform |
|
import time |
|
from pathlib import Path |
|
import tempfile |
|
import glob |
|
import traceback |
|
|
|
def ensure_dir(dir_path): |
|
"""Ensure directory exists""" |
|
Path(dir_path).mkdir(parents=True, exist_ok=True) |
|
|
|
def check_dependencies(): |
|
"""Check if required dependencies are available""" |
|
missing_deps = [] |
|
|
|
|
|
try: |
|
result = subprocess.run([sys.executable, "-m", "nuitka", "--version"], capture_output=True, text=True) |
|
if result.returncode != 0: |
|
missing_deps.append("nuitka") |
|
except: |
|
missing_deps.append("nuitka") |
|
|
|
|
|
result = subprocess.run(["which", "patchelf"], capture_output=True) |
|
if result.returncode != 0: |
|
missing_deps.append("patchelf") |
|
|
|
|
|
result = subprocess.run(["which", "gcc"], capture_output=True) |
|
if result.returncode != 0: |
|
missing_deps.append("gcc") |
|
|
|
return missing_deps |
|
|
|
def check_static_libpython(): |
|
"""Check if static libpython is available""" |
|
try: |
|
|
|
result = subprocess.run( |
|
[sys.executable, "-c", "import sysconfig; print(sysconfig.get_config_var('LIBDIR'))"], |
|
capture_output=True, text=True |
|
) |
|
if result.returncode == 0: |
|
libdir = result.stdout.strip() |
|
|
|
static_libs = glob.glob(os.path.join(libdir, "libpython*.a")) |
|
return len(static_libs) > 0 |
|
except: |
|
pass |
|
return False |
|
|
|
def get_current_python_version(): |
|
"""Get the current Python version for compatibility notes""" |
|
return f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}" |
|
|
|
def get_nuitka_version(): |
|
"""Get the current Nuitka version to handle different command line options""" |
|
try: |
|
result = subprocess.run([sys.executable, "-m", "nuitka", "--version"], |
|
capture_output=True, text=True) |
|
if result.returncode == 0: |
|
version_line = result.stdout.strip().split('\n')[0] |
|
|
|
version = version_line.split()[-1] |
|
return version |
|
return "not installed" |
|
except: |
|
return "not installed" |
|
|
|
def create_progress_bar(progress, status_text="", show_percentage=True): |
|
"""Create an HTML progress bar""" |
|
percentage = max(0, min(100, progress * 100)) |
|
|
|
color = "bg-green-500" if percentage == 100 else ("bg-yellow-500" if percentage > 50 else "bg-blue-500") |
|
|
|
html = f""" |
|
<div style="width: 100%; background-color: #f0f0f0; border-radius: 10px; padding: 3px; box-shadow: inset 0 2px 4px rgba(0,0,0,0.1);"> |
|
<div style="width: {percentage}%; background: linear-gradient(90deg, #4CAF50 0%, #45a049 100%); |
|
height: 25px; border-radius: 8px; transition: width 0.3s ease-in-out; display: flex; align-items: center; justify-content: center;"> |
|
<span style="color: white; font-weight: bold; font-size: 12px;"> |
|
{f'{percentage:.1f}%' if show_percentage else ''} |
|
</span> |
|
</div> |
|
</div> |
|
<div style="margin-top: 8px; text-align: center; color: #333; font-size: 14px; font-weight: 500;"> |
|
{status_text} |
|
</div> |
|
""" |
|
return html |
|
|
|
def install_system_packages(packages_content, progress_callback=None): |
|
"""Note about system packages in HF Spaces - they cannot be installed""" |
|
if not packages_content.strip(): |
|
return "No system packages specified." |
|
|
|
|
|
packages_list = [line.strip() for line in packages_content.strip().split('\n') |
|
if line.strip() and not line.strip().startswith('#')] |
|
|
|
if packages_list: |
|
return f"β System packages cannot be installed in Hugging Face Spaces:\n{', '.join(packages_list)}\n\nβΉοΈ HF Spaces runs in a containerized environment without sudo access.\nThese packages need to be pre-installed in the Docker image or available as Python packages." |
|
return "No system packages specified." |
|
|
|
def install_nuitka_if_missing(): |
|
"""Try to install Nuitka if it's missing""" |
|
try: |
|
|
|
result = subprocess.run([sys.executable, "-m", "nuitka", "--version"], capture_output=True) |
|
if result.returncode == 0: |
|
return True, "Nuitka is already installed" |
|
|
|
|
|
print("Installing Nuitka...") |
|
result = subprocess.run([sys.executable, "-m", "pip", "install", "nuitka"], |
|
capture_output=True, text=True) |
|
|
|
if result.returncode == 0: |
|
return True, "Nuitka installed successfully" |
|
else: |
|
return False, f"Failed to install Nuitka: {result.stderr}" |
|
except Exception as e: |
|
return False, f"Error installing Nuitka: {str(e)}" |
|
|
|
def find_compiled_binary(output_dir, output_filename): |
|
"""Find the compiled binary, checking different possible paths""" |
|
|
|
direct_path = os.path.join(output_dir, output_filename) |
|
if os.path.exists(direct_path): |
|
return direct_path |
|
|
|
|
|
dist_path = os.path.join(output_dir, "user_script.dist", output_filename) |
|
if os.path.exists(dist_path): |
|
return dist_path |
|
|
|
|
|
patterns = [ |
|
os.path.join(output_dir, "**", output_filename), |
|
os.path.join(output_dir, "**", "user_script"), |
|
os.path.join(output_dir, "**", "*.bin"), |
|
os.path.join(output_dir, "**", "*.exe") |
|
] |
|
|
|
for pattern in patterns: |
|
matches = glob.glob(pattern, recursive=True) |
|
if matches: |
|
return matches[0] |
|
|
|
return None |
|
|
|
def compile_with_nuitka(code, requirements, packages, compilation_mode, output_extension, progress_callback=None): |
|
"""Compile Python code with Nuitka""" |
|
try: |
|
|
|
if progress_callback: |
|
progress_callback(0, "Starting compilation process...") |
|
|
|
|
|
nuitka_installed, nuitka_msg = install_nuitka_if_missing() |
|
if not nuitka_installed: |
|
error_summary = f"""# β Nuitka Not Available |
|
|
|
## Error: |
|
{nuitka_msg} |
|
|
|
## Solution: |
|
Nuitka needs to be installed first. In Hugging Face Spaces, this should be done in the requirements.txt file: |
|
|
|
``` |
|
nuitka |
|
``` |
|
|
|
Add this to your requirements.txt and redeploy the space.""" |
|
return error_summary, None, nuitka_msg, False |
|
|
|
if progress_callback: |
|
progress_callback(0.05, "Checking environment...") |
|
|
|
|
|
nuitka_version = get_nuitka_version() |
|
|
|
|
|
has_static_libpython = check_static_libpython() |
|
|
|
|
|
missing_deps = check_dependencies() |
|
|
|
if progress_callback: |
|
progress_callback(0.1, "Setting up compilation directories...") |
|
|
|
|
|
job_id = str(uuid.uuid4()) |
|
base_dir = os.path.join(os.getcwd(), "user_code") |
|
job_dir = os.path.join(base_dir, job_id) |
|
output_dir = os.path.join(os.getcwd(), "compiled_output", job_id) |
|
|
|
|
|
ensure_dir(job_dir) |
|
ensure_dir(output_dir) |
|
|
|
if progress_callback: |
|
progress_callback(0.15, "Processing packages...") |
|
|
|
|
|
packages_result = install_system_packages(packages) |
|
|
|
|
|
script_path = os.path.join(job_dir, "user_script.py") |
|
with open(script_path, "w") as f: |
|
f.write(code) |
|
|
|
if progress_callback: |
|
progress_callback(0.2, "Installing Python requirements...") |
|
|
|
|
|
install_result = "No Python requirements specified." |
|
if requirements.strip(): |
|
req_path = os.path.join(job_dir, "requirements.txt") |
|
with open(req_path, "w") as f: |
|
f.write(requirements) |
|
|
|
try: |
|
install_process = subprocess.run( |
|
[sys.executable, "-m", "pip", "install", "--no-cache-dir", "-r", req_path], |
|
capture_output=True, |
|
text=True |
|
) |
|
|
|
if install_process.returncode == 0: |
|
install_result = "β
Python requirements installed successfully." |
|
else: |
|
install_result = f"β οΈ Installation completed with warnings. Return code: {install_process.returncode}\n{install_process.stderr}" |
|
except Exception as e: |
|
install_result = f"β Error installing requirements: {str(e)}" |
|
return install_result, None, f"Error: {str(e)}", False |
|
|
|
if progress_callback: |
|
progress_callback(0.3, "Building compilation command...") |
|
|
|
|
|
if compilation_mode == "Maximum Compatibility (Recommended)": |
|
cmd = [ |
|
sys.executable, "-m", "nuitka", |
|
"--standalone", |
|
"--onefile", |
|
"--show-progress", |
|
"--remove-output", |
|
"--follow-imports", |
|
"--assume-yes-for-downloads", |
|
"--python-flag=no_site", |
|
"--python-flag=no_warnings", |
|
script_path, |
|
f"--output-dir={output_dir}" |
|
] |
|
|
|
|
|
if has_static_libpython: |
|
cmd.append("--static-libpython=yes") |
|
|
|
mode_name = "Maximum Compatibility Binary" |
|
|
|
elif compilation_mode == "Portable Binary": |
|
cmd = [ |
|
sys.executable, "-m", "nuitka", |
|
"--show-progress", |
|
"--remove-output", |
|
"--assume-yes-for-downloads", |
|
"--python-flag=no_site", |
|
"--python-flag=no_warnings", |
|
script_path, |
|
f"--output-dir={output_dir}" |
|
] |
|
mode_name = "Portable Non-Standalone" |
|
|
|
else: |
|
cmd = [ |
|
sys.executable, "-m", "nuitka", |
|
"--standalone", |
|
"--onefile", |
|
"--show-progress", |
|
"--remove-output", |
|
"--assume-yes-for-downloads", |
|
"--python-flag=no_site", |
|
"--python-flag=no_warnings", |
|
script_path, |
|
f"--output-dir={output_dir}" |
|
] |
|
mode_name = "Standalone Binary" |
|
|
|
|
|
if progress_callback: |
|
progress_callback(0.4, "Executing Nuitka compilation...") |
|
|
|
process = subprocess.Popen( |
|
cmd, |
|
stdout=subprocess.PIPE, |
|
stderr=subprocess.STDOUT, |
|
text=True, |
|
bufsize=1, |
|
universal_newlines=True |
|
) |
|
|
|
|
|
compile_output = "" |
|
line_count = 0 |
|
|
|
|
|
for line in iter(process.stdout.readline, ''): |
|
compile_output += line |
|
line_count += 1 |
|
|
|
|
|
progress_val = 0.4 + (min(line_count / 200, 0.55) * 0.5) |
|
status_text = "Compiling..." |
|
|
|
if "INFO:" in line: |
|
status_text = f"INFO: {line.strip()[:60]}..." |
|
elif "PROCESSING" in line: |
|
status_text = f"Processing: {line.strip()[:60]}..." |
|
elif "Optimizing" in line: |
|
status_text = f"Optimizing: {line.strip()[:60]}..." |
|
elif "Creating" in line: |
|
status_text = f"Creating: {line.strip()[:60]}..." |
|
|
|
if progress_callback: |
|
progress_callback(progress_val, status_text) |
|
|
|
process.wait() |
|
|
|
if progress_callback: |
|
progress_callback(0.9, "Finalizing compilation...") |
|
|
|
|
|
output_filename = f"user_script{output_extension}" |
|
binary_path = find_compiled_binary(output_dir, output_filename) |
|
|
|
|
|
if not binary_path: |
|
|
|
patterns = [ |
|
os.path.join(output_dir, "user_script"), |
|
os.path.join(output_dir, "user_script.bin"), |
|
os.path.join(output_dir, "**", "user_script"), |
|
os.path.join(output_dir, "**", "*.bin"), |
|
] |
|
|
|
for pattern in patterns: |
|
matches = glob.glob(pattern, recursive=True) |
|
if matches: |
|
binary_path = matches[0] |
|
break |
|
|
|
if process.returncode == 0 and binary_path: |
|
|
|
try: |
|
file_process = subprocess.run(["file", binary_path], capture_output=True, text=True) |
|
binary_info = file_process.stdout |
|
except: |
|
binary_info = "Binary file (unable to get detailed info)" |
|
|
|
|
|
try: |
|
ldd_process = subprocess.run(["ldd", binary_path], capture_output=True, text=True) |
|
if "not a dynamic executable" in ldd_process.stderr or "statically linked" in ldd_process.stdout: |
|
linking_info = "β
Statically linked - fully portable!" |
|
else: |
|
|
|
if ldd_process.returncode == 0: |
|
libs = ldd_process.stdout.count("=>") |
|
linking_info = f"π Dynamically linked ({libs} libraries) - designed for maximum compatibility" |
|
else: |
|
linking_info = "βΉοΈ Compiled binary - should work on compatible systems" |
|
except: |
|
linking_info = "βΉοΈ Compiled binary created successfully" |
|
|
|
|
|
if output_extension in ['.bin', '.sh'] and not binary_path.endswith(output_extension): |
|
new_binary_path = binary_path + output_extension |
|
shutil.move(binary_path, new_binary_path) |
|
binary_path = new_binary_path |
|
|
|
|
|
os.chmod(binary_path, 0o755) |
|
|
|
|
|
current_python = get_current_python_version() |
|
|
|
|
|
static_status = "Yes" if has_static_libpython else "No" |
|
file_size = os.path.getsize(binary_path) / 1024 |
|
binary_basename = os.path.basename(binary_path) |
|
|
|
|
|
result_summary = f"""# β
Compilation Successful! |
|
|
|
## Compilation Details: |
|
- **Mode**: {mode_name} |
|
- **Nuitka Version**: {nuitka_version} |
|
- **Exit Code**: {process.returncode} |
|
- **Output Path**: {binary_path} |
|
- **File Size**: {file_size:.2f} KB |
|
- **Compiled with Python**: {current_python} |
|
- **Static Libpython Available**: {static_status} |
|
- **Linking**: {linking_info} |
|
|
|
## Environment Results: |
|
**System Packages**: {packages_result} |
|
**Python Requirements**: {install_result} |
|
|
|
## Binary Information: |
|
{binary_info} |
|
|
|
## π Portability Notes: |
|
- This binary was compiled with maximum compatibility settings |
|
- Using --onefile for single-file distribution |
|
- Added --assume-yes-for-downloads for automatic dependency resolution |
|
- Used --python-flag=no_site to reduce system dependencies |
|
- Should work on most compatible Linux systems |
|
|
|
## π Usage Instructions: |
|
```bash |
|
chmod +x {binary_basename} |
|
./{binary_basename} |
|
``` |
|
|
|
## β οΈ HF Spaces Notice: |
|
This binary was compiled in a Hugging Face Spaces environment. For best results: |
|
1. Download the binary to your local system |
|
2. Make it executable: `chmod +x filename` |
|
3. Run it in a compatible Linux environment""" |
|
|
|
if progress_callback: |
|
progress_callback(1.0, "Compilation successful! π") |
|
return result_summary, binary_path, compile_output, True |
|
else: |
|
|
|
error_summary = f"""# β Compilation Failed |
|
|
|
## Error Details: |
|
- **Exit Code**: {process.returncode} |
|
- **Mode Attempted**: {mode_name} |
|
|
|
## Environment Results: |
|
**System Packages**: {packages_result} |
|
**Python Requirements**: {install_result} |
|
|
|
## Compilation Output: |
|
``` |
|
{compile_output} |
|
``` |
|
|
|
## Possible Solutions: |
|
1. Check your code for syntax errors |
|
2. Ensure all imports are available in requirements.txt |
|
3. Try a different compilation mode |
|
4. Review the compilation logs above |
|
|
|
## Missing Dependencies: |
|
{', '.join(missing_deps) if missing_deps else 'None detected'} |
|
|
|
## Environment Info: |
|
- **Nuitka Version**: {nuitka_version} |
|
- **Python Version**: {current_python} |
|
- **Platform**: {platform.platform()}""" |
|
if progress_callback: |
|
progress_callback(1.0, "Compilation failed β") |
|
return error_summary, None, compile_output, False |
|
|
|
except Exception as e: |
|
|
|
error_trace = traceback.format_exc() |
|
error_summary = f"""# β Compilation Error |
|
|
|
## Error: |
|
{str(e)} |
|
|
|
## Full Error Trace: |
|
``` |
|
{error_trace} |
|
``` |
|
|
|
## Environment Status: |
|
- **Nuitka Version**: {get_nuitka_version()} |
|
- **Python Version**: {get_current_python_version()} |
|
- **Working Directory**: {os.getcwd()} |
|
|
|
## Troubleshooting Steps: |
|
1. Check if Nuitka is properly installed |
|
2. Verify your code syntax |
|
3. Check available disk space |
|
4. Try with simpler code first""" |
|
if progress_callback: |
|
progress_callback(1.0, "Error occurred β") |
|
return error_summary, None, f"Error: {str(e)}\n\n{error_trace}", False |
|
|
|
def run_compiled_binary(binary_path): |
|
"""Run the compiled binary and return the output""" |
|
if not binary_path or not os.path.exists(binary_path): |
|
return "β No binary available to run." |
|
|
|
try: |
|
|
|
os.chmod(binary_path, 0o755) |
|
|
|
|
|
process = subprocess.run( |
|
[binary_path], |
|
capture_output=True, |
|
text=True, |
|
timeout=30, |
|
cwd=os.path.dirname(binary_path) |
|
) |
|
|
|
output = "" |
|
if process.stdout: |
|
output += f"## [STDOUT]\n```\n{process.stdout}\n```\n" |
|
if process.stderr: |
|
output += f"## [STDERR]\n```\n{process.stderr}\n```\n" |
|
if process.returncode != 0: |
|
output += f"## [EXIT CODE]\n{process.returncode}\n" |
|
|
|
if not output: |
|
output = "β
Binary executed successfully with no output." |
|
else: |
|
output = "## π§ͺ Execution Results\n" + output |
|
|
|
return output |
|
except subprocess.TimeoutExpired: |
|
return "β±οΈ **Execution timed out after 30 seconds.**\n\nThis might indicate:\n- The program is waiting for input\n- An infinite loop\n- Long-running computation" |
|
except Exception as e: |
|
return f"β **Error running the binary:**\n\n```\n{str(e)}\n```" |
|
|
|
|
|
with gr.Blocks(title="Nuitka Python Compiler for HF Spaces", theme=gr.themes.Soft()) as app: |
|
gr.Markdown("# π Nuitka Python Compiler (HF Spaces Edition)") |
|
gr.Markdown("Convert your Python code into portable executables using Nuitka, optimized for Hugging Face Spaces.") |
|
|
|
|
|
has_static = check_static_libpython() |
|
missing_deps = check_dependencies() |
|
|
|
if "nuitka" in missing_deps: |
|
gr.Markdown("β οΈ **Nuitka is not installed!** Add 'nuitka' to your requirements.txt file.") |
|
|
|
if has_static: |
|
gr.Markdown("π― **Static Libpython Available!** Maximum portability enabled.") |
|
else: |
|
gr.Markdown("π§ **Using alternative portable options.** Static libpython not available.") |
|
|
|
if [dep for dep in missing_deps if dep != "nuitka"]: |
|
gr.Markdown(f"β οΈ **Missing dependencies:** {', '.join([dep for dep in missing_deps if dep != 'nuitka'])}") |
|
else: |
|
gr.Markdown("β
**All required dependencies available!**") |
|
|
|
|
|
gr.Markdown(""" |
|
> βΉοΈ **Running in Hugging Face Spaces**: System packages cannot be installed. |
|
> All required dependencies should be pre-installed in the environment. |
|
""") |
|
|
|
with gr.Tabs(): |
|
with gr.TabItem("π§ Compiler"): |
|
with gr.Row(): |
|
with gr.Column(scale=2): |
|
code_input = gr.Code( |
|
value="""# Your Python code here |
|
print('Hello from compiled Python!') |
|
print('This is a smart-compiled binary!') |
|
|
|
# This will work with automatic compatibility detection |
|
import os, sys |
|
print(f'Running from: {os.getcwd()}') |
|
print(f'Python executable: {sys.executable}') |
|
print('Compilation was optimized for your environment!') |
|
|
|
# Simple example with user input |
|
name = input('What is your name? ') |
|
print(f'Hello, {name}!')""", |
|
language="python", |
|
label="Your Python Code", |
|
lines=20 |
|
) |
|
|
|
with gr.Column(scale=1): |
|
with gr.Tabs(): |
|
with gr.TabItem("Python Requirements"): |
|
requirements_input = gr.Textbox( |
|
placeholder="""# Add your Python dependencies here |
|
# Example: |
|
# numpy==1.24.0 |
|
# pandas==2.0.0 |
|
# requests>=2.28.0 |
|
# matplotlib |
|
# pillow""", |
|
lines=8, |
|
label="requirements.txt content" |
|
) |
|
|
|
with gr.TabItem("System Packages"): |
|
gr.Markdown("β οΈ **System packages cannot be installed in HF Spaces**") |
|
packages_input = gr.Textbox( |
|
placeholder="""# System packages (for reference only) |
|
# These cannot be installed in HF Spaces |
|
# build-essential |
|
# libssl-dev |
|
# ffmpeg""", |
|
lines=8, |
|
label="packages.txt content (Reference Only)", |
|
interactive=True |
|
) |
|
|
|
|
|
compilation_mode = gr.Dropdown( |
|
choices=[ |
|
"Maximum Compatibility (Recommended)", |
|
"Portable Binary", |
|
"Standalone Binary" |
|
], |
|
value="Maximum Compatibility (Recommended)", |
|
label="Compilation Mode" |
|
) |
|
|
|
output_extension = gr.Dropdown( |
|
choices=[".bin", ".sh"], |
|
value=".bin", |
|
label="Output File Extension" |
|
) |
|
|
|
gr.Markdown(f"π **Compiling with Python {get_current_python_version()}**") |
|
gr.Markdown(f"π§ **Nuitka Version**: {get_nuitka_version()}") |
|
|
|
if check_static_libpython(): |
|
gr.Markdown("π **Static libpython will be used!**") |
|
else: |
|
gr.Markdown("π§ **Using portable compilation flags**") |
|
|
|
compile_btn = gr.Button("π Compile with Nuitka", variant="primary") |
|
|
|
|
|
with gr.Column(visible=False) as progress_section: |
|
gr.Markdown("### π Compilation Progress") |
|
progress_bar = gr.HTML(create_progress_bar(0, "Ready to compile...")) |
|
|
|
|
|
with gr.Column(visible=False) as results_section: |
|
with gr.Accordion("π Compilation Results", open=True): |
|
result_summary = gr.Markdown() |
|
with gr.Accordion("π Compilation Logs", open=False): |
|
compile_logs = gr.Textbox(label="Detailed Compilation Output", lines=15) |
|
download_file = gr.File(label="π₯ Download Compiled Binary") |
|
|
|
|
|
with gr.Row(): |
|
run_btn = gr.Button("π§ͺ Test Run Binary", variant="secondary") |
|
run_output = gr.Markdown(label="Execution Output") |
|
|
|
|
|
current_binary_path = gr.State(None) |
|
compilation_success = gr.State(False) |
|
|
|
def update_progress_display(progress, status): |
|
"""Update the progress bar display""" |
|
return gr.update(value=create_progress_bar(progress, status)) |
|
|
|
def handle_compilation(code, requirements, packages, mode, extension): |
|
try: |
|
|
|
yield ( |
|
gr.update(visible=True), |
|
gr.update(value=create_progress_bar(0, "Initializing compilation...")), |
|
gr.update(visible=False), |
|
gr.update(), |
|
gr.update(), |
|
gr.update(visible=False), |
|
None, |
|
False |
|
) |
|
|
|
|
|
def progress_callback(progress, status): |
|
|
|
pass |
|
|
|
|
|
compilation_started = False |
|
|
|
|
|
yield ( |
|
gr.update(visible=True), |
|
gr.update(value=create_progress_bar(0.1, "Checking Nuitka installation...")), |
|
gr.update(visible=False), |
|
gr.update(), |
|
gr.update(), |
|
gr.update(visible=False), |
|
None, |
|
False |
|
) |
|
|
|
|
|
yield ( |
|
gr.update(visible=True), |
|
gr.update(value=create_progress_bar(0.2, "Setting up environment...")), |
|
gr.update(visible=False), |
|
gr.update(), |
|
gr.update(), |
|
gr.update(visible=False), |
|
None, |
|
False |
|
) |
|
|
|
|
|
yield ( |
|
gr.update(visible=True), |
|
gr.update(value=create_progress_bar(0.3, "Installing requirements...")), |
|
gr.update(visible=False), |
|
gr.update(), |
|
gr.update(), |
|
gr.update(visible=False), |
|
None, |
|
False |
|
) |
|
|
|
|
|
for i in range(4, 9): |
|
yield ( |
|
gr.update(visible=True), |
|
gr.update(value=create_progress_bar(i/10, "Compiling with Nuitka...")), |
|
gr.update(visible=False), |
|
gr.update(), |
|
gr.update(), |
|
gr.update(visible=False), |
|
None, |
|
False |
|
) |
|
time.sleep(0.5) |
|
|
|
|
|
summary, binary_path, logs, success = compile_with_nuitka( |
|
code, requirements, packages, mode, extension |
|
) |
|
|
|
|
|
yield ( |
|
gr.update(visible=True), |
|
gr.update(value=create_progress_bar(1.0, "Compilation complete!" if success else "Compilation failed")), |
|
gr.update(visible=True), |
|
gr.update(value=summary), |
|
gr.update(value=logs), |
|
gr.update(visible=False), |
|
None, |
|
False |
|
) |
|
|
|
if success and binary_path: |
|
|
|
download_filename = f"compiled_program{extension}" |
|
download_path = os.path.join(os.path.dirname(binary_path), download_filename) |
|
shutil.copy2(binary_path, download_path) |
|
|
|
|
|
yield ( |
|
gr.update(visible=True), |
|
gr.update(value=create_progress_bar(1.0, "β
Compilation successful! Ready to download.")), |
|
gr.update(visible=True), |
|
gr.update(value=summary), |
|
gr.update(value=logs), |
|
gr.update(value=download_path, visible=True), |
|
binary_path, |
|
True |
|
) |
|
else: |
|
|
|
yield ( |
|
gr.update(visible=True), |
|
gr.update(value=create_progress_bar(1.0, "β Compilation failed. Check logs for details.")), |
|
gr.update(visible=True), |
|
gr.update(value=summary), |
|
gr.update(value=logs), |
|
gr.update(visible=False), |
|
None, |
|
False |
|
) |
|
|
|
except Exception as e: |
|
error_trace = traceback.format_exc() |
|
error_summary = f"""# β Unexpected Error in Compilation Handler |
|
|
|
## Error: |
|
{str(e)} |
|
|
|
## Full Trace: |
|
``` |
|
{error_trace} |
|
```""" |
|
yield ( |
|
gr.update(visible=True), |
|
gr.update(value=create_progress_bar(1.0, "β Error occurred during compilation")), |
|
gr.update(visible=True), |
|
gr.update(value=error_summary), |
|
gr.update(value=f"Error: {str(e)}"), |
|
gr.update(visible=False), |
|
None, |
|
False |
|
) |
|
|
|
def handle_run(binary_path): |
|
if binary_path: |
|
output = run_compiled_binary(binary_path) |
|
return gr.update(value=output) |
|
else: |
|
return gr.update(value="β No binary available to run.") |
|
|
|
compile_btn.click( |
|
handle_compilation, |
|
inputs=[code_input, requirements_input, packages_input, compilation_mode, output_extension], |
|
outputs=[progress_section, progress_bar, results_section, result_summary, compile_logs, download_file, current_binary_path, compilation_success] |
|
) |
|
|
|
run_btn.click( |
|
handle_run, |
|
inputs=[current_binary_path], |
|
outputs=[run_output] |
|
) |
|
|
|
with gr.TabItem("π How to Use"): |
|
gr.Markdown(""" |
|
## π― Smart Compilation in HF Spaces |
|
|
|
**Automatic Environment Detection** |
|
|
|
This app automatically detects your Python environment and chooses the best compilation strategy: |
|
- Uses static libpython if available (maximum portability) |
|
- Falls back to highly portable alternatives if not |
|
- Automatically handles missing dependencies |
|
- Optimized specifically for Hugging Face Spaces environment |
|
|
|
## π Usage Instructions |
|
|
|
### 1. Write Your Code |
|
- Enter your Python code in the code editor |
|
- Add any Python package requirements in the requirements tab |
|
|
|
### 2. Choose Compilation Mode |
|
- **Maximum Compatibility**: Best for most use cases (recommended) |
|
- **Portable Binary**: Smaller size, may need Python runtime |
|
- **Standalone Binary**: Includes all dependencies (larger) |
|
|
|
### 3. Compile |
|
- Click "Compile with Nuitka" |
|
- Watch the progress bar for compilation status |
|
- Wait for the compilation to complete |
|
- Download the resulting binary |
|
|
|
### 4. Run Your Binary |
|
|
|
```bash |
|
# On Linux (including WSL) |
|
chmod +x compiled_program.bin |
|
./compiled_program.bin |
|
|
|
# In WSL, copy to Linux filesystem first |
|
cp /mnt/c/Users/username/Downloads/compiled_program.bin ~/ |
|
cd ~ |
|
chmod +x compiled_program.bin |
|
./compiled_program.bin |
|
``` |
|
|
|
## β οΈ HF Spaces Limitations |
|
|
|
- **System packages cannot be installed** (no apt-get access) |
|
- All required system dependencies must be pre-installed |
|
- Compilation happens in a containerized environment |
|
- Binary execution may have resource limits |
|
|
|
## π Important Requirements |
|
|
|
For this app to work in HF Spaces, ensure your `requirements.txt` includes: |
|
``` |
|
gradio |
|
nuitka |
|
``` |
|
|
|
## π Compilation Modes Comparison |
|
|
|
| Mode | Size | Portability | Speed | Best For | |
|
|------|------|-------------|--------|----------| |
|
| Maximum Compatibility | Medium | Highest | Fast | Distribution | |
|
| Portable Binary | Small | High | Fastest | Quick testing | |
|
| Standalone Binary | Large | Highest | Medium | Isolated deployment | |
|
""") |
|
|
|
with gr.TabItem("βΉοΈ About"): |
|
gr.Markdown(f""" |
|
## π§ Smart Compilation Technology |
|
|
|
**How it works:** |
|
|
|
1. **Environment Detection**: Checks if static libpython is available |
|
2. **Adaptive Options**: Uses the best available compilation flags |
|
3. **Fallback Strategy**: Ensures compilation succeeds even without static linking |
|
4. **Automatic Dependencies**: Resolves missing dependencies automatically |
|
5. **HF Spaces Optimization**: Adapted for Hugging Face Spaces environment |
|
6. **Real-time Progress**: Shows compilation progress with a visual progress bar |
|
|
|
This approach maximizes compatibility across different Python environments while working within HF Spaces constraints. |
|
|
|
## β
What This Solves |
|
|
|
**Problems addressed:** |
|
|
|
- β
Static libpython not available error |
|
- β
Python version mismatches |
|
- β
HF Spaces environment limitations |
|
- β
WSL compatibility issues |
|
- β
Dependency resolution |
|
- β
Cross-environment portability |
|
- β
Real-time compilation feedback |
|
|
|
## βοΈ Current Environment Status |
|
|
|
``` |
|
Platform: {platform.platform()} |
|
Architecture: {platform.architecture()[0]} |
|
Machine: {platform.machine()} |
|
Python Version: {get_current_python_version()} |
|
Nuitka Version: {get_nuitka_version()} |
|
Static Libpython: {'β
Available' if check_static_libpython() else 'β Not Available'} |
|
Environment: Hugging Face Spaces |
|
``` |
|
|
|
## π Best Practices |
|
|
|
**Recommendations:** |
|
|
|
1. β
Always use "Maximum Compatibility" mode |
|
2. β
Test with simple scripts first |
|
3. β
Copy binaries to Linux filesystem in WSL |
|
4. β
Let the app automatically choose the best settings |
|
5. β
Check the compilation details for specific optimizations used |
|
6. β
Avoid system packages that require installation |
|
7. β
Include 'nuitka' in your requirements.txt for HF Spaces |
|
8. β
Watch the progress bar for compilation status |
|
|
|
## π§ Troubleshooting |
|
|
|
**Common Issues:** |
|
|
|
- **Import errors**: Check that all packages are in requirements.txt |
|
- **Binary won't run**: Ensure you're on a compatible Linux system |
|
- **Permission denied**: Run `chmod +x filename` before execution |
|
- **Missing libraries**: Try "Maximum Compatibility" mode |
|
- **Nothing happens on click**: Check that Nuitka is installed in requirements.txt |
|
- **Compilation stuck**: Check the progress bar and logs for details |
|
|
|
## π Future Improvements |
|
|
|
- Support for more compilation targets |
|
- Better error messages and diagnostics |
|
- Automatic dependency detection |
|
- Cross-platform compilation support |
|
- Enhanced progress tracking |
|
""") |
|
|
|
gr.Markdown("---") |
|
gr.Markdown("π€ Created by Claude 3.7 Sonnet | π Powered by Nuitka with Smart Compilation | βοΈ Optimized for HF Spaces") |
|
|
|
if __name__ == "__main__": |
|
|
|
ensure_dir("user_code") |
|
ensure_dir("compiled_output") |
|
|
|
|
|
if "nuitka" in check_dependencies(): |
|
print("WARNING: Nuitka is not installed. Please add 'nuitka' to your requirements.txt file.") |
|
|
|
app.launch() |