-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
9 changed files
with
297 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
[submodule "docs/_sass/minimal-mistakes"] | ||
path = docs/_sass/minimal-mistakes | ||
url = https://github.com/mmistakes/minimal-mistakes.git |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
`python-web-io` works by re-evaluating the target script after each user interaction, to progress the script to the next `input()`, etc. | ||
This means expensive functions may be called more than once per session. | ||
To reduce latency, a cache decorator is made available through the `python_web_io` module. | ||
The `@cache_to_file()` decorator accepts a string argument: `file_path`, which indicates where the cache (a `.pkl` file) should be stored. | ||
|
||
```python3 | ||
import python_web_io as io | ||
|
||
@io.cache_to_file('cache.pickle') | ||
def expensive_function(arg): | ||
# Calculate the result here | ||
return result | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
The appearance of generated pages are customisable via a `config.toml` file. | ||
Create a subdirectory `/.pythonwebio` relative to were the project will be called from, and create a `config.toml` file inside. | ||
``` | ||
. | ||
├── app.py | ||
├── .envrc | ||
└── .pythonwebio/ | ||
└── config.toml | ||
``` | ||
|
||
## Example `.envrc` | ||
If using a `config.toml` file in a non-default location (and deploying via `uvicorn`), set the config filepath via the `PYTHON_WEB_IO_CONFIG` environment variable. | ||
|
||
```bash | ||
# server env vars | ||
export PYTHON_WEB_IO_SECRET="" | ||
export PYTHON_WEB_IO_CONFIG=".pythonwebio/config.toml" # defaults to .pythonwebio/config.toml if not set | ||
``` | ||
|
||
## Example `config.toml` | ||
```TOML | ||
[script] | ||
filepath = "app.py" | ||
entrypoint = "main" | ||
|
||
[page] | ||
name = "Python web I/O App" | ||
icon = "🎯" | ||
css = [ | ||
"https://unpkg.com/[email protected]/normalize.css", | ||
"https://unpkg.com/simpledotcss/simple.min.css", | ||
] | ||
|
||
[about] | ||
author = "Zachary" | ||
profile = "https://github.com/Cutwell" | ||
description = "Generate a webpage as a GUI for a Python script, and serve from anywhere." | ||
|
||
[project] | ||
homepage = "https://github.com/Cutwell/python-web-io" | ||
license = "https://github.com/Cutwell/python-web-io/blob/main/LICENSE" | ||
issues = "https://github.com/Cutwell/python-web-io/issues/new" | ||
|
||
[server] | ||
debug = false | ||
``` | ||
|
||
## CLI | ||
`python_web_io` is designed to be run (for production and development) using `uvicorn`. Use the [uvicorn settings](https://www.uvicorn.org/settings/) docs for a reference to setup your server. | ||
|
||
For quick testing, `python_web_io` can also be ran as a Python script. Running directly spawns a `uvicorn` server with limited config options. Test mode is designed for evaluating multiple scripts quickly without editing a `config.toml` file, e.g.: testing the `/examples` scripts. | ||
|
||
```bash | ||
poetry run python_web_io --script="app.py" --config=".pythonwebio/config.toml" --host="localhost" --port=8000 | ||
``` | ||
|
||
||| | ||
|:---:|:---| | ||
|`--script`|Override `[script]` settings (format: `<filepath>:<entrypoint>`, e.g.: `app.py:main`) (default: None).| | ||
|`--config`|Override the `PYTHON_WEB_IO_CONFIG` environment variable and default `.pythonwebio/config.toml` config filepaths (default: `.pythonwebio/config.toml`).| | ||
|`--host`|Set the Uvicorn server host (default: `localhost`).| | ||
|`--port`|Set the Uvicorn server port (default: `8000`).| | ||
|
||
## `config.toml` Documentation | ||
### `[script]` | ||
||| | ||
|:---:|:---| | ||
|`filepath`|Rather than provide a script filepath via command line, an app filepath can also be defined here.| | ||
|`entrypoint`|Python scripts using the typical `if __name__ == '__main__': main()` will not run, as this check will fail. To resolve this, a function name can be supplied as an entrypoint for the script, and will be called to begin execution.| | ||
|
||
### `[page]` | ||
||| | ||
|:---:|:---| | ||
|`name`|Webapp name, used as the website / tab title.| | ||
|`icon`|Webapp icon, used in bookmarks and in the tab.| | ||
|`css`|Drop-in CSS styling is supported and encouraged to customise a web-app to user preference. A curated list of drop-in stylesheets can be found [here](https://github.com/sw-yx/spark-joy/blob/master/README.md#drop-in-css-frameworks). This option can be a list of stylesheets or a single item.| | ||
|
||
### `[about]` | ||
These options populate the `About` modal, accessible from the page footer. | ||
||| | ||
|:---:|:---| | ||
|`author`|Author's name / online alias.| | ||
|`profile`|Author social link, e,g.: GitHub, Twitter, etc.| | ||
|`description`|A short description to summarise the webapp.| | ||
|
||
### `[project]` | ||
These options populate the `Help` modal, accessible from the page footer. | ||
||| | ||
|:---:|:---| | ||
|`homepage`|A link to the project homepage, for instance on GitHub.| | ||
|`license`|A link to the license the project is distributed under.| | ||
|`issues`|A link to a forum / issue tracker for reporting bugs users may encounter.| | ||
|
||
### `[server]` | ||
These options are for the underlying FastAPI server: | ||
||| | ||
|:---:|:---| | ||
|`debug`|Boolean to enable debug mode.| |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
# <img src="https://raw.githubusercontent.com/Cutwell/python-web-io/main/.github/logo-40x38.png" style="width:40px;padding-right:10px;margin-bottom:-8px;" alt="Sticker of a cute yellow Python snake, representing the use of the Python programming language in this project."> Python Web I/O Website / Docs | ||
Generate a webpage as a GUI for a Python script, and serve from anywhere. | ||
|
||
[![PyPI](https://img.shields.io/pypi/v/python-web-io?style=flat-square)](https://pypi.org/project/python-web-io/) | ||
|
||
Install `python-web-io` locally using: | ||
```bash | ||
pip install python-web-io | ||
``` | ||
|
||
Or via `poetry` using: | ||
```bash | ||
poetry add python_web_io | ||
``` | ||
|
||
If evaluating / testing `python-web-io`, install dependencies for the example apps using: | ||
```bash | ||
poetry add python_web_io --with examples | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
`input()` and `print()` are overridden to add "magic" features. | ||
|
||
## `print()` | ||
|
||
* `print()` does not have any new parameters. | ||
* String inputs are evaluated into HTML using `markdownify`, meaning you can insert arbitrary HTML simply by printing the snippet, e.g.: `print("# Title") -> <p><h1>Title</h1></p>`. | ||
* Each `print()` statement wraps it's contents in `<p>...</p>` tags. If inlining elements, etc., print these tags together, e.g.: `print("# Title", "<span> Inline subtitle</span>") -> <p><h1>Title</h1><span> Inline subtitle</span></p>`. | ||
* Since strings are evaluated as raw HTML, this also allows use of `<img>`, `<style>` and `<script>` tags for more advanced app control (beware allowing unfiltered user input!). | ||
|
||
### Examples | ||
```python | ||
# display some text wrapped in `small` HTML tags | ||
print("<small>This is some subtext, it's not important.</small>") | ||
``` | ||
|
||
```html | ||
<small>This is some subtext, it's not important.</small> | ||
``` | ||
|
||
```python | ||
# inject a `style` tag into the page. | ||
print("<style>#logo{height: 32px; width: 32px}</style>") | ||
``` | ||
|
||
```html | ||
<style>#logo{height: 32px; width: 32px}</style> | ||
``` | ||
|
||
```python | ||
# display an image from an external src | ||
# control it's appearence (size, etc.) using the `logo` CSS id | ||
print("<img id='logo' src='https://cdn2.iconfinder.com/data/icons/activity-5/50/1F3A8-artist-palette-1024.png'>") | ||
``` | ||
|
||
```html | ||
<img id='logo' src='https://cdn2.iconfinder.com/data/icons/activity-5/50/1F3A8-artist-palette-1024.png'> | ||
``` | ||
|
||
## `input()` | ||
|
||
* `input()` has several new (_optional_) parameters for controlling what type of input is displayed. | ||
* By default, a textbox is used. | ||
* By setting `type`, the `<input>` tag can be customised (see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/Input#input_types for a comprehensive list of supported types). | ||
* By setting `options` (expecting a `list`) the `prompt` is used as a `<label>` and multiple `<input>`s of `type` (recommended types are `button`, `radio`) are created. Return value is whichever item from `option` list is selected. | ||
* By setting `attrs`, you can set additional attributes, such as `class` and `id`. Pass a dictionary in the format `{attribute: value}`, e.g.: `{'id': "myelement", 'class': "myclass"}`. | ||
|
||
### Custom attribute examples | ||
```python | ||
# return a number between 1-100 | ||
input("Pick a number between 1-100", type='range', attrs={'min': 0, 'max': 100}) | ||
``` | ||
|
||
```html | ||
<label>Pick a number between 1-100</label> | ||
<input type='range' min=0 max=100> | ||
``` | ||
|
||
```python | ||
# return a phone number, with client side validation for a correct pattern | ||
input("Enter your telephone number", type='tel', attrs={'pattern': "[0-9]{3}-[0-9]{2}-[0-9]{3}"}) | ||
``` | ||
|
||
```html | ||
<label>Enter your telephone number</label> | ||
<input type='tel' pattern=[0-9]{3}-[0-9]{2}-[0-9]{3}> | ||
``` | ||
|
||
## Custom option examples | ||
For input types such as `button`, `radio` and `checkbox`, multiple inputs can be rendered at once. This is an optional argument for all inputs, but will only have an effect for these listed input types. | ||
|
||
`button` and `radio` inputs can return a single option, or `None`, e.g.: | ||
```python | ||
# display 2 buttons | ||
# return the selected option (or None if `submit` button pressed instead) | ||
input("Do you like watermelon?", type='button', options=['yes', 'no']) | ||
``` | ||
|
||
```html | ||
<label>Do you like watermelon?</label> | ||
<input type='submit' value='yes'> <input type='button' value='no'> | ||
``` | ||
|
||
`checkbox` inputs can return any combination of options, or `None`, e.g.: | ||
```python | ||
# display 2 checkbox options | ||
# if more than 1 item selected, return selected items as a list (or None if `submit` button pressed instead) | ||
input("Which Sci-fi series do you like?", type='checkbox', options=['star wars', 'star trek']) | ||
``` | ||
|
||
```html | ||
<label>Which Sci-fi series do you like?</label> | ||
<input type='checkbox' value='star wars'> <input type='checkbox' value='star trek'> | ||
``` | ||
|
||
If an input is required (e.g.: `None` is not acceptable), set the `required` attribute, which will prevent form submission until an option is selected. E.g.: | ||
```python | ||
# display radio options (only 1 can be selected) | ||
# return the selected option (`required` is set, so an option must be selected) | ||
# if `submit` is pressed, the form will prompt the user to make a selection before being allowed to submit | ||
input("What's your thoughts on Marmite?", type='radio', options=['love it', 'hate it'], attrs={'required': True}) | ||
``` | ||
|
||
```html | ||
<label>What's your thoughts on Marmite?</label> | ||
<input type='radio' value='love it' required> <input type='radio' value='hate it' required> | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
After installing the project, some environment setup is required: | ||
|
||
## Required setup | ||
Create an `app.py` file containing your script, a `config.toml` setting the script filepath and entrypoint, and an `.envrc` file to store project secrets. (Note: remember to add `.envrc` to your `.gitignore`). Look for example apps in [`/examples`](https://github.com/Cutwell/python-web-io/tree/main/python-web-io/examples). | ||
``` | ||
. | ||
├── .envrc | ||
├── config.toml | ||
└── app.py | ||
``` | ||
|
||
Create the following simple `config.toml`: | ||
```toml | ||
[script] | ||
filepath = "app.py" | ||
entrypoint = "main" # if your app has no entrypoint, remove this parameter. | ||
``` | ||
|
||
Add the following environment variables to your `.envrc`. (Note: remember to activate the `.envrc` in your terminal using `direnv allow`) | ||
```bash | ||
# server env vars | ||
export PYTHON_WEB_IO_SECRET="" | ||
export PYTHON_WEB_IO_CONFIG="config.toml" # defaults to .pythonwebio/config.toml if not set | ||
``` | ||
|
||
Generate a random key for `PYTHON_WEB_IO_SECRET` using this python command line snippet: | ||
```bash | ||
python -c 'import secrets; print(secrets.token_hex())' | ||
``` | ||
|
||
If testing `wikipedia_assistant.py`, an OpenAI API key will also need to be set. | ||
```bash | ||
export OPENAI_API_KEY="" | ||
``` | ||
|
||
## Running the webapp | ||
We recommend running `python_web_io` using `uvicorn`: | ||
```bash | ||
poetry run uvicorn python_web_io.main:app | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
If you wish to serve local files, such as custom `.css` stylesheets or images, they will need to be placed in the `./static` folder, relative to the project root. | ||
``` | ||
. | ||
├── .envrc | ||
├── app.py | ||
├── .pythonwebio/ | ||
│ └── config.toml | ||
└── static/ | ||
``` | ||
|
||
Files stored here can be referenced using a `/static/image.png`-like URL. | ||
|
||
For security reasons (to prevent a malicious user accessing the entire server using a few `../../`), the underlying `FastAPI` server will not serve local files from outside this directory. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
remote_theme: "mmistakes/minimal-mistakes" | ||
plugins: | ||
- jekyll-seo-tag | ||
- jekyll-remote-theme |
Submodule minimal-mistakes
added at
8a67ce