func-to-web 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- func_to_web-0.1.0/LICENSE +21 -0
- func_to_web-0.1.0/PKG-INFO +326 -0
- func_to_web-0.1.0/README.md +309 -0
- func_to_web-0.1.0/examples/01_basic_division.py +8 -0
- func_to_web-0.1.0/examples/02_validated_division.py +11 -0
- func_to_web-0.1.0/examples/03_all_basic_types.py +24 -0
- func_to_web-0.1.0/examples/04_special_inputs.py +12 -0
- func_to_web-0.1.0/examples/05_dropdowns.py +17 -0
- func_to_web-0.1.0/examples/06_numeric_constraints.py +25 -0
- func_to_web-0.1.0/examples/07_string_validation.py +20 -0
- func_to_web-0.1.0/examples/08_image_blur.py +14 -0
- func_to_web-0.1.0/examples/09_image_effects.py +25 -0
- func_to_web-0.1.0/examples/10_image_resize.py +23 -0
- func_to_web-0.1.0/examples/11_plot_sine.py +26 -0
- func_to_web-0.1.0/examples/12_plot_comparison.py +29 -0
- func_to_web-0.1.0/examples/13_complete_form.py +42 -0
- func_to_web-0.1.0/examples/14_multiple_tools.py +48 -0
- func_to_web-0.1.0/func_to_web/__init__.py +448 -0
- func_to_web-0.1.0/func_to_web/templates/form.html +223 -0
- func_to_web-0.1.0/func_to_web/templates/index.html +29 -0
- func_to_web-0.1.0/func_to_web/templates/static/styles.css +511 -0
- func_to_web-0.1.0/func_to_web.egg-info/PKG-INFO +326 -0
- func_to_web-0.1.0/func_to_web.egg-info/SOURCES.txt +26 -0
- func_to_web-0.1.0/func_to_web.egg-info/dependency_links.txt +1 -0
- func_to_web-0.1.0/func_to_web.egg-info/requires.txt +5 -0
- func_to_web-0.1.0/func_to_web.egg-info/top_level.txt +4 -0
- func_to_web-0.1.0/pyproject.toml +30 -0
- func_to_web-0.1.0/setup.cfg +4 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2011-2025 The Bootstrap Authors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: func-to-web
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Transform any Python function into a web interface automatically
|
|
5
|
+
Author: Beltrán Offerrall
|
|
6
|
+
Project-URL: Homepage, https://github.com/offerrall/FuncToWeb
|
|
7
|
+
Project-URL: Repository, https://github.com/offerrall/FuncToWeb
|
|
8
|
+
Requires-Python: >=3.12
|
|
9
|
+
Description-Content-Type: text/markdown
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Requires-Dist: fastapi
|
|
12
|
+
Requires-Dist: uvicorn
|
|
13
|
+
Requires-Dist: pydantic
|
|
14
|
+
Requires-Dist: jinja2
|
|
15
|
+
Requires-Dist: python-multipart
|
|
16
|
+
Dynamic: license-file
|
|
17
|
+
|
|
18
|
+
# Func To Web
|
|
19
|
+
|
|
20
|
+
**Transform any Python function into a web interface automatically.**
|
|
21
|
+
|
|
22
|
+
func-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.
|
|
23
|
+
|
|
24
|
+
**The entire library is just 350 lines of Python and 700 lines of HTML/CSS/JS.** Simple, powerful, and easy to understand.
|
|
25
|
+
|
|
26
|
+

|
|
27
|
+
|
|
28
|
+
## Quick Start (Minimal Example)
|
|
29
|
+
|
|
30
|
+
```python
|
|
31
|
+
from func_to_web import run
|
|
32
|
+
|
|
33
|
+
def divide(a: int, b: int):
|
|
34
|
+
return a / b
|
|
35
|
+
|
|
36
|
+
run(divide)
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Open `http://127.0.0.1:8000` in your browser and you'll see an auto-generated form.
|
|
40
|
+
|
|
41
|
+

|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
## Installation
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
git clone https://github.com/offerrall/FuncToWeb
|
|
48
|
+
cd FuncToWeb
|
|
49
|
+
pip install .
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Or install directly from GitHub:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
pip install git+https://github.com/offerrall/FuncToWeb.git
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Examples
|
|
59
|
+
|
|
60
|
+
**Check the `examples/` folder** for 14+ 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:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
python examples/01_basic_division.py
|
|
64
|
+
python examples/08_image_blur.py
|
|
65
|
+
python examples/11_plot_sine.py
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+

|
|
69
|
+
|
|
70
|
+
## What Can You Do?
|
|
71
|
+
|
|
72
|
+
### Basic Types
|
|
73
|
+
|
|
74
|
+
All Python built-in types work out of the box:
|
|
75
|
+
|
|
76
|
+
```python
|
|
77
|
+
from func_to_web import run
|
|
78
|
+
from datetime import date, time
|
|
79
|
+
|
|
80
|
+
def example(
|
|
81
|
+
text: str, # Text input
|
|
82
|
+
number: int, # Integer input
|
|
83
|
+
decimal: float, # Decimal input
|
|
84
|
+
checkbox: bool, # Checkbox
|
|
85
|
+
birthday: date, # Date picker
|
|
86
|
+
meeting: time # Time picker
|
|
87
|
+
):
|
|
88
|
+
return "All basic types supported!"
|
|
89
|
+
|
|
90
|
+
run(example)
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+

|
|
94
|
+
|
|
95
|
+
### Special Input Types
|
|
96
|
+
|
|
97
|
+
```python
|
|
98
|
+
from func_to_web import run, Color, Email
|
|
99
|
+
|
|
100
|
+
def special_inputs(
|
|
101
|
+
favorite_color: Color, # Color picker
|
|
102
|
+
contact: Email # Email validation
|
|
103
|
+
):
|
|
104
|
+
return f"Color: {favorite_color}, Email: {contact}"
|
|
105
|
+
|
|
106
|
+
run(special_inputs)
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+

|
|
110
|
+
|
|
111
|
+
### File Uploads
|
|
112
|
+
|
|
113
|
+
```python
|
|
114
|
+
from func_to_web import run, ImageFile, DataFile, TextFile, DocumentFile
|
|
115
|
+
|
|
116
|
+
def process_files(
|
|
117
|
+
photo: ImageFile, # .png, .jpg, .jpeg, .gif, .webp
|
|
118
|
+
data: DataFile, # .csv, .xlsx, .xls, .json
|
|
119
|
+
notes: TextFile, # .txt, .md, .log
|
|
120
|
+
report: DocumentFile, # .pdf, .doc, .docx
|
|
121
|
+
):
|
|
122
|
+
return "Files uploaded!"
|
|
123
|
+
|
|
124
|
+
run(process_files)
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+

|
|
128
|
+
|
|
129
|
+
### Dropdowns
|
|
130
|
+
|
|
131
|
+
```python
|
|
132
|
+
from typing import Literal
|
|
133
|
+
from func_to_web import run
|
|
134
|
+
|
|
135
|
+
def preferences(
|
|
136
|
+
theme: Literal['light', 'dark', 'auto'],
|
|
137
|
+
language: Literal['en', 'es', 'fr']
|
|
138
|
+
):
|
|
139
|
+
return f"Theme: {theme}, Language: {language}"
|
|
140
|
+
|
|
141
|
+
run(preferences)
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+

|
|
145
|
+
|
|
146
|
+
### Constraints & Validation
|
|
147
|
+
|
|
148
|
+
```python
|
|
149
|
+
from typing import Annotated
|
|
150
|
+
from pydantic import Field
|
|
151
|
+
from func_to_web import run
|
|
152
|
+
|
|
153
|
+
def register(
|
|
154
|
+
age: Annotated[int, Field(ge=18, le=120)], # Min/max values
|
|
155
|
+
username: Annotated[str, Field(min_length=3, max_length=20)], # Length limits
|
|
156
|
+
rating: Annotated[float, Field(gt=0, lt=5)] # Exclusive bounds
|
|
157
|
+
):
|
|
158
|
+
return f"User {username}, age {age}, rating {rating}"
|
|
159
|
+
|
|
160
|
+
run(register)
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+

|
|
164
|
+
|
|
165
|
+
### Return Images & Plots
|
|
166
|
+
|
|
167
|
+
func-to-web automatically detects and displays images from PIL/Pillow and matplotlib:
|
|
168
|
+
|
|
169
|
+
```python
|
|
170
|
+
from func_to_web import run, ImageFile
|
|
171
|
+
from PIL import Image, ImageFilter
|
|
172
|
+
|
|
173
|
+
def blur_image(image: ImageFile, radius: int = 5):
|
|
174
|
+
img = Image.open(image)
|
|
175
|
+
return img.filter(ImageFilter.GaussianBlur(radius))
|
|
176
|
+
|
|
177
|
+
run(blur_image)
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+

|
|
181
|
+
|
|
182
|
+
```python
|
|
183
|
+
from func_to_web import run
|
|
184
|
+
import matplotlib.pyplot as plt
|
|
185
|
+
import numpy as np
|
|
186
|
+
|
|
187
|
+
def plot_sine(frequency: float = 1.0, amplitude: float = 1.0):
|
|
188
|
+
x = np.linspace(0, 10, 1000)
|
|
189
|
+
y = amplitude * np.sin(frequency * x)
|
|
190
|
+
|
|
191
|
+
fig, ax = plt.subplots(figsize=(10, 6))
|
|
192
|
+
ax.plot(x, y)
|
|
193
|
+
ax.grid(True)
|
|
194
|
+
return fig
|
|
195
|
+
|
|
196
|
+
run(plot_sine)
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+

|
|
200
|
+
|
|
201
|
+
## Run Multiple Functions
|
|
202
|
+
|
|
203
|
+
You 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 14.
|
|
204
|
+
|
|
205
|
+
```python
|
|
206
|
+
from func_to_web import run
|
|
207
|
+
|
|
208
|
+
def calculate_bmi(weight_kg: float, height_m: float):
|
|
209
|
+
"""Calculate Body Mass Index"""
|
|
210
|
+
bmi = weight_kg / (height_m ** 2)
|
|
211
|
+
return f"BMI: {bmi:.2f}"
|
|
212
|
+
|
|
213
|
+
def celsius_to_fahrenheit(celsius: float):
|
|
214
|
+
"""Convert Celsius to Fahrenheit"""
|
|
215
|
+
fahrenheit = (celsius * 9/5) + 32
|
|
216
|
+
return f"{celsius}°C = {fahrenheit}°F"
|
|
217
|
+
|
|
218
|
+
def reverse_text(text: str):
|
|
219
|
+
"""Reverse a string"""
|
|
220
|
+
return text[::-1]
|
|
221
|
+
|
|
222
|
+
# Pass a list of functions to create an index page
|
|
223
|
+
run([calculate_bmi, celsius_to_fahrenheit, reverse_text])
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+

|
|
227
|
+
|
|
228
|
+
## Features
|
|
229
|
+
|
|
230
|
+
### Input Types
|
|
231
|
+
- `int`, `float`, `str`, `bool` - Basic types
|
|
232
|
+
- `date`, `time` - Date and time pickers
|
|
233
|
+
- `Color` - Color picker with preview
|
|
234
|
+
- `Email` - Email validation
|
|
235
|
+
- `Literal[...]` - Dropdown selections
|
|
236
|
+
- `ImageFile`, `DataFile`, `TextFile`, `DocumentFile` - File uploads
|
|
237
|
+
|
|
238
|
+
### Validation
|
|
239
|
+
- **Numeric**: `ge`, `le`, `gt`, `lt` (min/max bounds)
|
|
240
|
+
- **String**: `min_length`, `max_length`, `pattern` (regex)
|
|
241
|
+
- **Required/Optional**: Automatic detection from type hints
|
|
242
|
+
- **Default values**: Set in function signature
|
|
243
|
+
|
|
244
|
+
### Output Types
|
|
245
|
+
- **Text/Numbers/Dicts** - Formatted as JSON
|
|
246
|
+
- **PIL Images** - Displayed as images
|
|
247
|
+
- **Matplotlib Figures** - Rendered as PNG
|
|
248
|
+
- **Any object** - Converted with `str()`
|
|
249
|
+
|
|
250
|
+
## Configuration
|
|
251
|
+
|
|
252
|
+
### One function:
|
|
253
|
+
```python
|
|
254
|
+
from func_to_web import run
|
|
255
|
+
|
|
256
|
+
def my_function(x: int):
|
|
257
|
+
return x * 2
|
|
258
|
+
|
|
259
|
+
run(my_function, host="127.0.0.1", port=5000, template_dir="my_templates")
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
### Multiple functions:
|
|
263
|
+
```python
|
|
264
|
+
from func_to_web import run
|
|
265
|
+
|
|
266
|
+
def func1(x: int):
|
|
267
|
+
return x * 2
|
|
268
|
+
|
|
269
|
+
def func2(y: str):
|
|
270
|
+
return y.upper()
|
|
271
|
+
|
|
272
|
+
run([func1, func2], host="127.0.0.1", port=5000, template_dir="my_templates")
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
**Parameters:**
|
|
276
|
+
- `func_or_list` - Single function or list of functions to serve
|
|
277
|
+
- `host` - Server host (default: `"0.0.0.0"`)
|
|
278
|
+
- `port` - Server port (default: `8000`)
|
|
279
|
+
- `template_dir` - Custom template directory (optional)
|
|
280
|
+
|
|
281
|
+
## Why func-to-web?
|
|
282
|
+
|
|
283
|
+
- **Minimalist** - Only 350 lines of Python + 700 lines of HTML/CSS/JS
|
|
284
|
+
- **Zero boilerplate** - Just type hints and you're done
|
|
285
|
+
- **Powerful** - Supports all common input types including files
|
|
286
|
+
- **Smart output** - Automatically displays images, plots, and data
|
|
287
|
+
- **Type-safe** - Full Pydantic validation
|
|
288
|
+
- **Client + server validation** - Instant feedback and robust checks
|
|
289
|
+
- **Batteries included** - 15+ examples in the `examples/` folder
|
|
290
|
+
- **Multi-function support** - Serve multiple tools from one server
|
|
291
|
+
|
|
292
|
+
## How It Works
|
|
293
|
+
|
|
294
|
+
1. **Analysis** - Inspects function signature using `inspect`
|
|
295
|
+
2. **Validation** - Validates type hints and constraints using `pydantic`
|
|
296
|
+
3. **Form Generation** - Builds HTML form fields from metadata
|
|
297
|
+
4. **File Handling** - Saves uploaded files to temp locations
|
|
298
|
+
5. **Server** - Runs FastAPI server with auto-generated routes
|
|
299
|
+
6. **Result Processing** - Detects return type and formats accordingly
|
|
300
|
+
7. **Display** - Shows results as text, JSON, images, or plots
|
|
301
|
+
|
|
302
|
+
## Requirements
|
|
303
|
+
|
|
304
|
+
- Python 3.12+
|
|
305
|
+
- FastAPI
|
|
306
|
+
- Uvicorn
|
|
307
|
+
- Pydantic
|
|
308
|
+
- Jinja2
|
|
309
|
+
- python-multipart
|
|
310
|
+
|
|
311
|
+
Optional for examples:
|
|
312
|
+
- Pillow (for image processing)
|
|
313
|
+
- Matplotlib (for plots)
|
|
314
|
+
- NumPy (for numerical computations)
|
|
315
|
+
|
|
316
|
+
## License
|
|
317
|
+
|
|
318
|
+
MIT
|
|
319
|
+
|
|
320
|
+
## Contributing
|
|
321
|
+
|
|
322
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
323
|
+
|
|
324
|
+
## Author
|
|
325
|
+
|
|
326
|
+
Beltrán Offerrall
|
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
# Func To Web
|
|
2
|
+
|
|
3
|
+
**Transform any Python function into a web interface automatically.**
|
|
4
|
+
|
|
5
|
+
func-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.
|
|
6
|
+
|
|
7
|
+
**The entire library is just 350 lines of Python and 700 lines of HTML/CSS/JS.** Simple, powerful, and easy to understand.
|
|
8
|
+
|
|
9
|
+

|
|
10
|
+
|
|
11
|
+
## Quick Start (Minimal Example)
|
|
12
|
+
|
|
13
|
+
```python
|
|
14
|
+
from func_to_web import run
|
|
15
|
+
|
|
16
|
+
def divide(a: int, b: int):
|
|
17
|
+
return a / b
|
|
18
|
+
|
|
19
|
+
run(divide)
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Open `http://127.0.0.1:8000` in your browser and you'll see an auto-generated form.
|
|
23
|
+
|
|
24
|
+

|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
## Installation
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
git clone https://github.com/offerrall/FuncToWeb
|
|
31
|
+
cd FuncToWeb
|
|
32
|
+
pip install .
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Or install directly from GitHub:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
pip install git+https://github.com/offerrall/FuncToWeb.git
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Examples
|
|
42
|
+
|
|
43
|
+
**Check the `examples/` folder** for 14+ 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:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
python examples/01_basic_division.py
|
|
47
|
+
python examples/08_image_blur.py
|
|
48
|
+
python examples/11_plot_sine.py
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+

|
|
52
|
+
|
|
53
|
+
## What Can You Do?
|
|
54
|
+
|
|
55
|
+
### Basic Types
|
|
56
|
+
|
|
57
|
+
All Python built-in types work out of the box:
|
|
58
|
+
|
|
59
|
+
```python
|
|
60
|
+
from func_to_web import run
|
|
61
|
+
from datetime import date, time
|
|
62
|
+
|
|
63
|
+
def example(
|
|
64
|
+
text: str, # Text input
|
|
65
|
+
number: int, # Integer input
|
|
66
|
+
decimal: float, # Decimal input
|
|
67
|
+
checkbox: bool, # Checkbox
|
|
68
|
+
birthday: date, # Date picker
|
|
69
|
+
meeting: time # Time picker
|
|
70
|
+
):
|
|
71
|
+
return "All basic types supported!"
|
|
72
|
+
|
|
73
|
+
run(example)
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+

|
|
77
|
+
|
|
78
|
+
### Special Input Types
|
|
79
|
+
|
|
80
|
+
```python
|
|
81
|
+
from func_to_web import run, Color, Email
|
|
82
|
+
|
|
83
|
+
def special_inputs(
|
|
84
|
+
favorite_color: Color, # Color picker
|
|
85
|
+
contact: Email # Email validation
|
|
86
|
+
):
|
|
87
|
+
return f"Color: {favorite_color}, Email: {contact}"
|
|
88
|
+
|
|
89
|
+
run(special_inputs)
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+

|
|
93
|
+
|
|
94
|
+
### File Uploads
|
|
95
|
+
|
|
96
|
+
```python
|
|
97
|
+
from func_to_web import run, ImageFile, DataFile, TextFile, DocumentFile
|
|
98
|
+
|
|
99
|
+
def process_files(
|
|
100
|
+
photo: ImageFile, # .png, .jpg, .jpeg, .gif, .webp
|
|
101
|
+
data: DataFile, # .csv, .xlsx, .xls, .json
|
|
102
|
+
notes: TextFile, # .txt, .md, .log
|
|
103
|
+
report: DocumentFile, # .pdf, .doc, .docx
|
|
104
|
+
):
|
|
105
|
+
return "Files uploaded!"
|
|
106
|
+
|
|
107
|
+
run(process_files)
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+

|
|
111
|
+
|
|
112
|
+
### Dropdowns
|
|
113
|
+
|
|
114
|
+
```python
|
|
115
|
+
from typing import Literal
|
|
116
|
+
from func_to_web import run
|
|
117
|
+
|
|
118
|
+
def preferences(
|
|
119
|
+
theme: Literal['light', 'dark', 'auto'],
|
|
120
|
+
language: Literal['en', 'es', 'fr']
|
|
121
|
+
):
|
|
122
|
+
return f"Theme: {theme}, Language: {language}"
|
|
123
|
+
|
|
124
|
+
run(preferences)
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+

|
|
128
|
+
|
|
129
|
+
### Constraints & Validation
|
|
130
|
+
|
|
131
|
+
```python
|
|
132
|
+
from typing import Annotated
|
|
133
|
+
from pydantic import Field
|
|
134
|
+
from func_to_web import run
|
|
135
|
+
|
|
136
|
+
def register(
|
|
137
|
+
age: Annotated[int, Field(ge=18, le=120)], # Min/max values
|
|
138
|
+
username: Annotated[str, Field(min_length=3, max_length=20)], # Length limits
|
|
139
|
+
rating: Annotated[float, Field(gt=0, lt=5)] # Exclusive bounds
|
|
140
|
+
):
|
|
141
|
+
return f"User {username}, age {age}, rating {rating}"
|
|
142
|
+
|
|
143
|
+
run(register)
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+

|
|
147
|
+
|
|
148
|
+
### Return Images & Plots
|
|
149
|
+
|
|
150
|
+
func-to-web automatically detects and displays images from PIL/Pillow and matplotlib:
|
|
151
|
+
|
|
152
|
+
```python
|
|
153
|
+
from func_to_web import run, ImageFile
|
|
154
|
+
from PIL import Image, ImageFilter
|
|
155
|
+
|
|
156
|
+
def blur_image(image: ImageFile, radius: int = 5):
|
|
157
|
+
img = Image.open(image)
|
|
158
|
+
return img.filter(ImageFilter.GaussianBlur(radius))
|
|
159
|
+
|
|
160
|
+
run(blur_image)
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+

|
|
164
|
+
|
|
165
|
+
```python
|
|
166
|
+
from func_to_web import run
|
|
167
|
+
import matplotlib.pyplot as plt
|
|
168
|
+
import numpy as np
|
|
169
|
+
|
|
170
|
+
def plot_sine(frequency: float = 1.0, amplitude: float = 1.0):
|
|
171
|
+
x = np.linspace(0, 10, 1000)
|
|
172
|
+
y = amplitude * np.sin(frequency * x)
|
|
173
|
+
|
|
174
|
+
fig, ax = plt.subplots(figsize=(10, 6))
|
|
175
|
+
ax.plot(x, y)
|
|
176
|
+
ax.grid(True)
|
|
177
|
+
return fig
|
|
178
|
+
|
|
179
|
+
run(plot_sine)
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+

|
|
183
|
+
|
|
184
|
+
## Run Multiple Functions
|
|
185
|
+
|
|
186
|
+
You 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 14.
|
|
187
|
+
|
|
188
|
+
```python
|
|
189
|
+
from func_to_web import run
|
|
190
|
+
|
|
191
|
+
def calculate_bmi(weight_kg: float, height_m: float):
|
|
192
|
+
"""Calculate Body Mass Index"""
|
|
193
|
+
bmi = weight_kg / (height_m ** 2)
|
|
194
|
+
return f"BMI: {bmi:.2f}"
|
|
195
|
+
|
|
196
|
+
def celsius_to_fahrenheit(celsius: float):
|
|
197
|
+
"""Convert Celsius to Fahrenheit"""
|
|
198
|
+
fahrenheit = (celsius * 9/5) + 32
|
|
199
|
+
return f"{celsius}°C = {fahrenheit}°F"
|
|
200
|
+
|
|
201
|
+
def reverse_text(text: str):
|
|
202
|
+
"""Reverse a string"""
|
|
203
|
+
return text[::-1]
|
|
204
|
+
|
|
205
|
+
# Pass a list of functions to create an index page
|
|
206
|
+
run([calculate_bmi, celsius_to_fahrenheit, reverse_text])
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+

|
|
210
|
+
|
|
211
|
+
## Features
|
|
212
|
+
|
|
213
|
+
### Input Types
|
|
214
|
+
- `int`, `float`, `str`, `bool` - Basic types
|
|
215
|
+
- `date`, `time` - Date and time pickers
|
|
216
|
+
- `Color` - Color picker with preview
|
|
217
|
+
- `Email` - Email validation
|
|
218
|
+
- `Literal[...]` - Dropdown selections
|
|
219
|
+
- `ImageFile`, `DataFile`, `TextFile`, `DocumentFile` - File uploads
|
|
220
|
+
|
|
221
|
+
### Validation
|
|
222
|
+
- **Numeric**: `ge`, `le`, `gt`, `lt` (min/max bounds)
|
|
223
|
+
- **String**: `min_length`, `max_length`, `pattern` (regex)
|
|
224
|
+
- **Required/Optional**: Automatic detection from type hints
|
|
225
|
+
- **Default values**: Set in function signature
|
|
226
|
+
|
|
227
|
+
### Output Types
|
|
228
|
+
- **Text/Numbers/Dicts** - Formatted as JSON
|
|
229
|
+
- **PIL Images** - Displayed as images
|
|
230
|
+
- **Matplotlib Figures** - Rendered as PNG
|
|
231
|
+
- **Any object** - Converted with `str()`
|
|
232
|
+
|
|
233
|
+
## Configuration
|
|
234
|
+
|
|
235
|
+
### One function:
|
|
236
|
+
```python
|
|
237
|
+
from func_to_web import run
|
|
238
|
+
|
|
239
|
+
def my_function(x: int):
|
|
240
|
+
return x * 2
|
|
241
|
+
|
|
242
|
+
run(my_function, host="127.0.0.1", port=5000, template_dir="my_templates")
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
### Multiple functions:
|
|
246
|
+
```python
|
|
247
|
+
from func_to_web import run
|
|
248
|
+
|
|
249
|
+
def func1(x: int):
|
|
250
|
+
return x * 2
|
|
251
|
+
|
|
252
|
+
def func2(y: str):
|
|
253
|
+
return y.upper()
|
|
254
|
+
|
|
255
|
+
run([func1, func2], host="127.0.0.1", port=5000, template_dir="my_templates")
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
**Parameters:**
|
|
259
|
+
- `func_or_list` - Single function or list of functions to serve
|
|
260
|
+
- `host` - Server host (default: `"0.0.0.0"`)
|
|
261
|
+
- `port` - Server port (default: `8000`)
|
|
262
|
+
- `template_dir` - Custom template directory (optional)
|
|
263
|
+
|
|
264
|
+
## Why func-to-web?
|
|
265
|
+
|
|
266
|
+
- **Minimalist** - Only 350 lines of Python + 700 lines of HTML/CSS/JS
|
|
267
|
+
- **Zero boilerplate** - Just type hints and you're done
|
|
268
|
+
- **Powerful** - Supports all common input types including files
|
|
269
|
+
- **Smart output** - Automatically displays images, plots, and data
|
|
270
|
+
- **Type-safe** - Full Pydantic validation
|
|
271
|
+
- **Client + server validation** - Instant feedback and robust checks
|
|
272
|
+
- **Batteries included** - 15+ examples in the `examples/` folder
|
|
273
|
+
- **Multi-function support** - Serve multiple tools from one server
|
|
274
|
+
|
|
275
|
+
## How It Works
|
|
276
|
+
|
|
277
|
+
1. **Analysis** - Inspects function signature using `inspect`
|
|
278
|
+
2. **Validation** - Validates type hints and constraints using `pydantic`
|
|
279
|
+
3. **Form Generation** - Builds HTML form fields from metadata
|
|
280
|
+
4. **File Handling** - Saves uploaded files to temp locations
|
|
281
|
+
5. **Server** - Runs FastAPI server with auto-generated routes
|
|
282
|
+
6. **Result Processing** - Detects return type and formats accordingly
|
|
283
|
+
7. **Display** - Shows results as text, JSON, images, or plots
|
|
284
|
+
|
|
285
|
+
## Requirements
|
|
286
|
+
|
|
287
|
+
- Python 3.12+
|
|
288
|
+
- FastAPI
|
|
289
|
+
- Uvicorn
|
|
290
|
+
- Pydantic
|
|
291
|
+
- Jinja2
|
|
292
|
+
- python-multipart
|
|
293
|
+
|
|
294
|
+
Optional for examples:
|
|
295
|
+
- Pillow (for image processing)
|
|
296
|
+
- Matplotlib (for plots)
|
|
297
|
+
- NumPy (for numerical computations)
|
|
298
|
+
|
|
299
|
+
## License
|
|
300
|
+
|
|
301
|
+
MIT
|
|
302
|
+
|
|
303
|
+
## Contributing
|
|
304
|
+
|
|
305
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
306
|
+
|
|
307
|
+
## Author
|
|
308
|
+
|
|
309
|
+
Beltrán Offerrall
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
from datetime import date, time
|
|
2
|
+
|
|
3
|
+
from func_to_web import run
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def showcase_types(
|
|
7
|
+
name: str = "John",
|
|
8
|
+
age: int = 25,
|
|
9
|
+
height: float = 1.75,
|
|
10
|
+
active: bool = True,
|
|
11
|
+
birthday: date = date(2000, 1, 1),
|
|
12
|
+
alarm: time = time(7, 30)
|
|
13
|
+
):
|
|
14
|
+
"""Demonstrates all basic types"""
|
|
15
|
+
return {
|
|
16
|
+
"name": name,
|
|
17
|
+
"age": age,
|
|
18
|
+
"height": height,
|
|
19
|
+
"active": active,
|
|
20
|
+
"birthday": str(birthday),
|
|
21
|
+
"alarm": str(alarm)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
run(showcase_types)
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
from func_to_web import Color, Email, run
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def create_account(
|
|
5
|
+
email: Email,
|
|
6
|
+
favorite_color: Color = "#3b82f6",
|
|
7
|
+
secondary_color: Color = "#10b981"
|
|
8
|
+
):
|
|
9
|
+
"""Create account with special input types"""
|
|
10
|
+
return f"Account created for {email} with colors {favorite_color} and {secondary_color}"
|
|
11
|
+
|
|
12
|
+
run(create_account)
|