{"id":31698827,"url":"https://github.com/offerrall/functoweb","last_synced_at":"2026-01-07T23:19:44.164Z","repository":{"id":318231792,"uuid":"1069977697","full_name":"offerrall/FuncToWeb","owner":"offerrall","description":"Transform any Python function into a web interface automatically. ","archived":false,"fork":false,"pushed_at":"2025-10-05T22:58:25.000Z","size":603,"stargazers_count":4,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-10-06T00:26:44.185Z","etag":null,"topics":["css","fastapi","html","javascript","jinja2","pydantic","python"],"latest_commit_sha":null,"homepage":"","language":"HTML","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/offerrall.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-10-05T02:14:10.000Z","updated_at":"2025-10-05T23:44:45.000Z","dependencies_parsed_at":"2025-10-06T00:36:48.087Z","dependency_job_id":null,"html_url":"https://github.com/offerrall/FuncToWeb","commit_stats":null,"previous_names":["offerrall/functoweb"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/offerrall/FuncToWeb","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/offerrall%2FFuncToWeb","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/offerrall%2FFuncToWeb/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/offerrall%2FFuncToWeb/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/offerrall%2FFuncToWeb/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/offerrall","download_url":"https://codeload.github.com/offerrall/FuncToWeb/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/offerrall%2FFuncToWeb/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279002012,"owners_count":26083258,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-09T02:00:07.460Z","response_time":59,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["css","fastapi","html","javascript","jinja2","pydantic","python"],"created_at":"2025-10-08T19:11:04.930Z","updated_at":"2025-10-11T22:01:43.969Z","avatar_url":"https://github.com/offerrall.png","language":"HTML","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Func To Web 0.4.4\n\n**Transform any Python function into a web interface automatically.**\n\nfunc-to-web is a minimalist library that generates web UIs from your Python functions with zero boilerplate. Just add type hints, call `run()`, and you're done.\n\nSimple, powerful, and easy to understand.\n\n![func-to-web Demo](images/functoweb.jpg)\n\n## Quick Start (Minimal Example)\n\n```python\nfrom func_to_web import run\n\ndef divide(a: int, b: int):\n    return a / b\n\nrun(divide)\n```\n\nOpen `http://127.0.0.1:8000` in your browser and you'll see an auto-generated form.\n\n![func-to-web Demo](images/demo.jpg)\n\u003e **Note:** Try entering `b=0` to see how errors are automatically handled and displayed in the UI.\n\n## Installation\n\n```bash\npip install func-to-web\n```\n\n## Examples\n\n**Check the `examples/` folder** for 16+ complete, runnable examples covering everything from basic forms to image processing and data visualization. Each example is a single Python file you can run immediately:\n\n```bash\npython examples/01_basic_division.py\npython examples/08_image_blur.py\npython examples/11_plot_sine.py\n```\n\n![Examples Preview](images/full_example.jpeg)\n\n## What Can You Do?\n\n### Basic Types\n\nAll Python built-in types work out of the box:\n\n```python\nfrom func_to_web import run\nfrom datetime import date, time\n\ndef example(\n    text: str,              # Text input\n    number: int,            # Integer input\n    decimal: float,         # Decimal input\n    checkbox: bool,         # Checkbox\n    birthday: date,         # Date picker\n    meeting: time           # Time picker\n):\n    return \"All basic types supported!\"\n\nrun(example)\n```\n\n![Basic Types](images/basic.jpg)\n\n### Optional Parameters\n\nUse `Type | None` syntax to make parameters optional with a visual toggle switch:\n\n```python\nfrom func_to_web import run\nfrom func_to_web.types import OptionalEnabled, OptionalDisabled, Email\nfrom typing import Annotated\nfrom pydantic import Field\n\ndef create_user(\n    username: str,  # Required field\n    \n    # Automatic behavior (standard Python syntax)\n    age: int | None = None,  # Disabled (no default value)\n    city: str | None = \"Madrid\",  # Enabled (has default value)\n    \n    # Explicit control (new in 0.4.4)\n    email: Email | OptionalEnabled,  # Always starts enabled\n    phone: str | OptionalDisabled,  # Always starts disabled\n    bio: Annotated[str, Field(max_length=500)] | OptionalDisabled = \"Dev\",  # Disabled despite default\n):\n    result = f\"Username: {username}\"\n    if age:\n        result += f\", Age: {age}\"\n    if city:\n        result += f\", City: {city}\"\n    if email:\n        result += f\", Email: {email}\"\n    if phone:\n        result += f\", Phone: {phone}\"\n    if bio:\n        result += f\", Bio: {bio}\"\n    return result\n\nrun(create_user)\n```\n\n![Optional Parameters](images/optional_fields.jpg)\n\n**How it works:**\n- Optional fields display a toggle switch to enable/disable them\n- **Automatic mode** (`Type | None`): Enabled if has default value, disabled if no default\n- **Explicit mode** (`Type | OptionalEnabled/OptionalDisabled`): You control the initial state, overriding defaults\n- Disabled fields automatically send `None` to your function\n- Works with all field types and constraints\n\nUse automatic mode for standard Python conventions, or explicit mode when you need precise control over which fields start active.\n\n### Special Input Types\n\n```python\nfrom func_to_web import run\nfrom func_to_web.types import Color, Email\n\ndef create_account(\n    email: Email,\n    favorite_color: Color = \"#3b82f6\",  # Default blue, required\n    secondary_color: Color | None = \"#10b981\",  # Default green, optional\n    tertiary_color: Color | None = None,  # No default, optional\n):\n    \"\"\"Create account with special input types\"\"\"\n    return f\"Account for {email} with colors {favorite_color}, {secondary_color}, {tertiary_color}\"\n\nrun(create_account)\n```\n\n![Color Picker](images/color.jpg)\n\n### File Uploads\n\n```python\nfrom func_to_web import run\nfrom func_to_web.types import ImageFile, DataFile, TextFile, DocumentFile\n\ndef process_files(\n    photo: ImageFile,       # .png, .jpg, .jpeg, .gif, .webp\n    data: DataFile,         # .csv, .xlsx, .xls, .json\n    notes: TextFile,        # .txt, .md, .log\n    report: DocumentFile    # .pdf, .doc, .docx\n):\n    return \"Files uploaded!\"\n\nrun(process_files)\n```\n\n![File Upload](images/files.jpg)\n\n### Upload Progress \u0026 Performance\n\nWhen uploading files, you'll see:\n- Real-time progress bar (0-100%)\n- File size display (e.g., \"Uploading 245 MB of 1.2 GB\")\n- Upload speed and status messages\n- Processing indicator when server is working\n\n![Upload Progress](images/upload.jpg)\n\n**Performance highlights:**\n- **Optimized streaming**: Files are uploaded in 8MB chunks, reducing memory usage\n- **Large file support**: Efficiently handles files from 1GB to 10GB+\n- **High speeds**: ~237 MB/s on localhost, ~100-115 MB/s on Gigabit Ethernet\n- **Low memory footprint**: Constant memory usage regardless of file size\n- **No blocking**: Submit button disabled during upload to prevent duplicates\n\nThe progress bar uses `XMLHttpRequest` to track upload progress in real-time, providing instant feedback to users even with very large files.\n\n### Dropdowns (Static)\n\nUse `Literal` for fixed dropdown options:\n\n```python\nfrom typing import Literal\nfrom func_to_web import run\n\ndef preferences(\n    theme: Literal['light', 'dark', 'auto'],\n    language: Literal['en', 'es', 'fr']\n):\n    return f\"Theme: {theme}, Language: {language}\"\n\nrun(preferences)\n```\n\n![Dropdowns](images/drop.jpg)\n\nAll options must be literals (strings, numbers, booleans) and all options must be of the same type.\n\n### Dropdowns (Dynamic)\n\nUse functions inside `Literal` to generate options dynamically at runtime:\n\n```python\nfrom typing import Literal\nfrom random import sample\nfrom func_to_web import run\n\nTHEMES = ['light', 'dark', 'auto', 'neon', 'retro']\n\ndef get_themes():\n    \"\"\"Generate random subset of themes\"\"\"\n    return sample(THEMES, k=3)\n\ndef configure_app(\n    theme: Literal[get_themes],  # type: ignore\n):\n    \"\"\"Configure app with dynamic dropdown\"\"\"\n    return f\"Selected theme: {theme}\"\n\nrun(configure_app)\n```\n\n**Use cases for dynamic dropdowns:**\n- Load options from a database\n- Fetch data from an API\n- Generate options based on time or context\n- Filter available choices based on business logic\n\nThe function is called each time the form is generated, ensuring fresh options every time. The `# type: ignore` comment is needed to suppress type checker warnings, the 15_dynamic_dropdown.py example demonstrates this.\n\n### Constraints \u0026 Validation\n\n```python\nfrom typing import Annotated\nfrom func_to_web import run\nfrom func_to_web.types import Field\n\ndef register(\n    age: Annotated[int, Field(ge=18, le=120)],  # Min/max values\n    username: Annotated[str, Field(min_length=3, max_length=20)],  # Length limits\n    rating: Annotated[float, Field(gt=0, lt=5)]  # Exclusive bounds\n):\n    return f\"User {username}, age {age}, rating {rating}\"\n\nrun(register)\n```\n\n![Validation](images/limits.jpg)\n\n### Return Images \u0026 Plots\n\nfunc-to-web automatically detects and displays images from PIL/Pillow and matplotlib:\n\n```python\nfrom func_to_web import run\nfrom func_to_web.types import ImageFile\nfrom PIL import Image, ImageFilter\n\ndef blur_image(image: ImageFile, radius: int = 5):\n    img = Image.open(image)\n    return img.filter(ImageFilter.GaussianBlur(radius))\n\nrun(blur_image)\n```\n\n![Image Processing](images/image.jpg)\n\n```python\nfrom func_to_web import run\nimport matplotlib.pyplot as plt\nimport numpy as np\n\ndef plot_sine(frequency: float = 1.0, amplitude: float = 1.0):\n    x = np.linspace(0, 10, 1000)\n    y = amplitude * np.sin(frequency * x)\n    \n    fig, ax = plt.subplots(figsize=(10, 6))\n    ax.plot(x, y)\n    ax.grid(True)\n    return fig\n\nrun(plot_sine)\n```\n\n![Plot Result](images/plot.jpg)\n\n## Run Multiple Functions \n\nYou can serve multiple functions simultaneously. When passing a list of functions, func-to-web automatically creates a responsive index page where users can select the tool they want to use. This is demonstrated in Example 15.\n\n```python\nfrom func_to_web import run\n\ndef calculate_bmi(weight_kg: float, height_m: float):\n    \"\"\"Calculate Body Mass Index\"\"\"\n    bmi = weight_kg / (height_m ** 2)\n    return f\"BMI: {bmi:.2f}\"\n\ndef celsius_to_fahrenheit(celsius: float):\n    \"\"\"Convert Celsius to Fahrenheit\"\"\"\n    fahrenheit = (celsius * 9/5) + 32\n    return f\"{celsius}°C = {fahrenheit}°F\"\n\ndef reverse_text(text: str):\n    \"\"\"Reverse a string\"\"\"\n    return text[::-1]\n\n# Pass a list of functions to create an index page\nrun([calculate_bmi, celsius_to_fahrenheit, reverse_text])\n```\n\n![Multiple Tools](images/multiple.jpg)\n\n## Features\n\n### Input Types\n- `int`, `float`, `str`, `bool` - Basic types\n- `date`, `time` - Date and time pickers\n- `Color` - Color picker with preview\n- `Email` - Email validation\n- `Literal[...]` - Dropdown selections, static or dynamic\n- `ImageFile`, `DataFile`, `TextFile`, `DocumentFile` - File uploads\n- (All input types support optional `| None` for toggles)\n\n### Validation\n- **Numeric**: `ge`, `le`, `gt`, `lt` (min/max bounds)\n- **String**: `min_length`, `max_length`, `pattern` (regex)\n- **Default values**: Set in function signature\n\n### Output Types\n- **Text/Numbers/Dicts** - Formatted as JSON\n- **PIL Images** - Displayed as images\n- **Matplotlib Figures** - Rendered as PNG\n- **Any object** - Converted with `str()`\n\n### Upload Features\n- **Progress tracking** - Real-time progress bar and percentage\n- **File size display** - Human-readable format (KB, MB, GB)\n- **Status messages** - \"Uploading...\", \"Processing...\", etc.\n- **Optimized streaming** - 8MB chunks for efficient memory usage\n- **Large file support** - Handles multi-gigabyte files efficiently\n- **Error handling** - Network errors, timeouts, and cancellations\n\n## Configuration\n\n### One function:\n```python\nfrom func_to_web import run\n\ndef my_function(x: int):\n    return x * 2\n\nrun(my_function, host=\"127.0.0.1\", port=5000, template_dir=\"my_templates\")\n```\n\n### Multiple functions:\n```python\nfrom func_to_web import run\n\ndef func1(x: int): \n    return x * 2\n\ndef func2(y: str): \n    return y.upper()\n\nrun([func1, func2], host=\"127.0.0.1\", port=5000, template_dir=\"my_templates\")\n```\n\n**Parameters:**\n- `func_or_list` - Single function or list of functions to serve\n- `host` - Server host (default: `\"0.0.0.0\"`)\n- `port` - Server port (default: `8000`)\n- `template_dir` - Custom template directory (optional)\n\n## Why func-to-web?\n\n- **Minimalist** - Under 1500 lines total, backend + frontend + docs\n- **Zero boilerplate** - Just type hints and you're done\n- **Powerful** - Supports all common input types including files\n- **Smart output** - Automatically displays images, plots, and data\n- **Type-safe** - Full Pydantic validation\n- **Optional toggles** - Enable/disable optional fields easily\n- **Client + server validation** - Instant feedback and robust checks\n- **Well-tested** - 307 unit tests ensuring reliability\n- **Batteries included** - 15+ examples in the `examples/` folder\n- **Multi-function support** - Serve multiple tools from one server\n- **Optimized performance** - Streaming uploads, progress tracking, low memory usage\n\n## How It Works\n\n1. **Analysis** - Inspects function signature using `inspect`\n2. **Validation** - Validates type hints and constraints using `pydantic`\n3. **Form Generation** - Builds HTML form fields from metadata\n4. **File Handling** - Streams uploaded files to temp locations in chunks\n5. **Server** - Runs FastAPI server with auto-generated routes\n6. **Result Processing** - Detects return type and formats accordingly\n7. **Display** - Shows results as text, JSON, images, or plots\n8. **Progress Tracking** - Real-time feedback during uploads and processing\n\n## Testing \u0026 Quality Assurance\n\n**307 unit tests** ensuring reliability:\n\n### Test Coverage\n\n- **130 tests** - `analyze()`: Function signature analysis, type detection, constraint extraction\n- **88 tests** - `validate_params()`: Type conversion, constraint validation, optional toggles\n- **88 tests** - `build_form_fields()`: HTML field generation, format conversion, edge cases\n\n**Edge cases covered**: Unicode (😀🚀), negative/large numbers (1e100), scientific notation, leap years, boundary values, empty lists, mixed types\n\n### Running Tests\n\n```bash\n# All tests\npytest tests/ -v\n\n# Specific module\npytest tests/test_analyze.py -v\npytest tests/test_validate.py -v\npytest tests/test_build_form_fields.py -v\n```\n\n## Requirements\n\n- Python 3.12+\n- FastAPI\n- Uvicorn\n- Pydantic\n- Jinja2\n- python-multipart\n\nOptional for examples:\n- Pillow (for image processing)\n- Matplotlib (for plots)\n- NumPy (for numerical computations)\n\nDevelopment dependencies:\n- pytest (for running tests)\n\n## Performance Notes\n\n### Startup Performance\nThe first request after server start may take ~300-500ms due to:\n- Template compilation\n- Module imports\n- FastAPI initialization\n\nSubsequent requests are typically \u003c5ms. This is normal Python/FastAPI behavior.\n\n## License\n\nMIT\n\n## Contributing\n\nContributions are welcome! Please feel free to submit a Pull Request.\n\nWhen contributing, please:\n- Add tests for new features\n- Ensure all existing tests pass (`pytest tests/ -v`)\n- Update documentation as needed\n\n## Author\n\nBeltrán Offerrall","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fofferrall%2Ffunctoweb","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fofferrall%2Ffunctoweb","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fofferrall%2Ffunctoweb/lists"}