## Intro
BrutalPyWebUI is a brutalist async Python framework for building
utilitarian desktop applications that require a tried-and-true
web-based cross-platform interface with realtime capabilities and no hassle.
## Simple and focused
BrutalPyWebUI exposes a basic web interface bound to a host and port of
your choice, and provides you a selection of very basic utility
functions to update the DOM on the frontend from your Python backend,
as well as a few global javascript functions for quickly
grabbing data from the interface on the frontend and sending
it to the backend.
## Light on dependencies
BrutalPyWebUI depends only on:
- [hypercorn (the ASGI server)](https://github.com/pgjones/hypercorn),
- [Quart (a Flask replacement for asyncio)](https://github.com/pallets/quart)
## Light on assets
BrutalPyWebUI bundles only:
- [a default favicon (the Pyton logo)](https://www.favicon.cc/?action=icon&file_id=831343)
- [a default font (JetBrains Mono Regular)](https://github.com/JetBrains/JetBrainsMono)
- [a default css reset (Normalize.css)](https://github.com/necolas/normalize.css)
Note that you can disable all three separately.
## Flexible UI design
There are three main approaches in regards to actually
building the user interface:
### Utilitarian
Write most of your logic in Python and use the UI
as a barebones interface that sends events by calling
the special global `_wuiEvent()` function from a script
or an onclick attribute, etc., and gets updated
on-demand, from your Python code, by calling any
of the `wui.el_*()` or `wui.pg_*()` utility functions.
### Embrace Modernity
Use Python-land as more of a complementary backend and
inject heavier javascript and css that can contain
anything from logic and styling for basic web components
to the bundled code of a full-fledged React app. Note that
any js you inject using `base_js` in the constructor
is injected after the definitions for the global `_wui*()` functions
so they will not be overriden and you can call them normally
from within your app. You could also just use `asset_handler`
to serve your scripts along with other files.
### Anything in the middle / anything you can think of
Self-explanatory.
The idea is to give the developer a sturdy platform to
build upon, expanding freely as needed, without
highly specific enforced patterns.
## Installation
```bash
pip install brutalpywebui
```
## Basic usage
```python
import typing as t
from brutalpywebui import BrutalPyWebUI
example_html = '''
<input id="inp_regular_txt" value="Updated value" />
<button onclick="_wuiEvent('btn_press', _wuiVal('#inp_regular_txt'))">{{ button_text }}</button>
<div>Result: <span id="txt_result">Old value</span></div>
<div>Ticker: <span id="txt_ticker">{{ ticker_text }}</span></div>
'''
example_css = '''
body {
display: flex;
flex-direction: column;
align-items: center;
padding: 30px 20px;
}
body > * {
width: 100%;
height: 24px;
max-width: 300px;
margin-bottom: 10px;
padding: 0;
border: none;
outline: none;
box-sizing: border-box;
}
body > input {
border: 1px solid black;
padding: 0 3px;
}
'''
wui: BrutalPyWebUI | None = None
ticker = 0
async def on_background():
global ticker
ticker += 1
await wui.el_set_text(["#txt_ticker"], str(ticker))
async def on_event(event: str, data: t.Any):
match event:
case "btn_press":
await wui.el_set_text(["#txt_result"], data)
case _:
pass
async def on_init():
# this uses Jinja2 templates
await wui.el_set_html_templ(
["body"],
example_html,
button_text="Press me!",
ticker_text=str(ticker),
)
wui = BrutalPyWebUI(
page_title="MyApp",
init_handler=on_init,
event_handler=on_event,
background_handler=on_background,
background_interval=5.2, # seconds
base_css=example_css,
)
# these are the default host and port
# specified here for the sake of the example
wui.run(host="localhost", port=7865)
```
## Technical note
The run() method uses asyncio.run() and is meant
to be run on the main thread. The app will not be
served until run() is called.
## Python reference
### `__init__`
```python
async def on_init():
pass
async def on_event(name: str, data: t.Any):
pass
async def on_asset(asset_name: str):
# served at yourhost:port/assets/<asset_name>
asset_bytes, content_type = mock_read_asset(asset_name)
return asset_bytes, content_type
wui = BrutalPyWebUI(
page_title="MyApp",
init_handler=on_init,
event_handler=on_event,
asset_handler=on_asset,
background_handler=on_background,
background_interval=5.2, # seconds
base_css=lambda: mock_read_asset("bundle.css") # or pass str directly,
base_js=lambda: mock_read_asset("bundle.js") # or pass str directly,
inject_python_favicon=True,
inject_jetbrains_font=True,
inject_normalize_css=True,
page_websocket_use_tls=False,
page_lang=lambda: "en", # or pass str directly,
page_encoding=lambda: "UTF-8", # or pass str directly,
page_viewport=lambda: "width=device-width, initial-scale=1.0", # or pass str directly,
debug=False, # print debug logs where applicable
)
```
- `page_title(str|()->str)` -- title of the page.
- `init_handler(async()->None)` -- coroutine to run on init.
- `event_handler(async(str,Any)->None)` -- coroutine to handle your custom events
from the frontend.
- `asset_handler(async(str)->(asset_content(Any), content_type(str)))` -- coroutine
to handle serving your custom assets for the frontend.
- `background_handler(async()->None)` -- coroutine called periodically in the background
controlled by the background_interval arg.
- `background_interval(float)` -- how often (in seconds with subsecond precision)
to call the background_handler.
- `base_css(str|()->str)` -- css string to inject in the main css file
for the frontend.
- `base_js(str|()->str)` -- js string to inject into the main js file
for the frontend.
- `inject_python_favicon(bool)` -- whether to use a default favicon.
- `inject_jetbrains_font(bool)` -- whether to use the JetBrains Mono Regular font.
- `inject_normalize_css(bool)` -- whether to inject NormalizeCSS into the main css file
for the frontend.
- `page_websocket_use_tls(bool)` -- whether to use wss instead of ws for the websocket
connection on the frontend.
- `page_lang(str)` -- the lang attribute for the html tag.
- `page_encoding(str)` -- the encoding of the page, normally you should not change this.
- `page_viewport(str)` -- the value for the viewport meta tag, normally you should not
change this unless having responsive issues with CSS.
- `debug(bool)` -- whether to print debug logs where applicable.
### `el_append_text`
Append text to the current el.innerText of one or more targets.
Affects all connected instances.
```python
await wui.el_set_text(['#my_element'], 'Hello')
await wui.el_append_text(['#my_element'], ' there')
```
- `selectors(list[str])` -- querySelectorAll selectors for targeting.
- `content(str)` -- a string containing any text.
### `el_append_value`
Append text to the current el.value of one or more targets.
Affects all connected instances.
```python
await wui.el_set_value(['#my_input'], 'Hello')
await wui.el_append_value(['#my_input'], ' there')
```
- `selectors(list[str])` -- querySelectorAll selectors for targeting.
- `content(str)` -- a string containing any text.
### `el_class_add`
Add a class to el.classList on one or more targets.
Affects all connected instances.
```python
await wui.el_class_add(['#my_element'], 'my_custom_class')
```
- `selectors(list[str])` -- querySelectorAll selectors for targeting.
- `name(str)` -- name of the class.
### `el_class_remove`
Remove a class from el.classList on one or more targets.
Affects all connected instances.
```python
await wui.el_class_remove(['#my_element'], 'my_custom_class')
```
- `selectors(list[str])` -- querySelectorAll selectors for targeting.
- `name(str)` -- name of the class.
### `el_disable`
Disable an element using el.disabled.
Affects all connected instances.
```python
await wui.el_disable(['#some_input', '#some_button'])
```
- `selectors(list[str])` -- querySelectorAll selectors for targeting.
### `el_enable`
Enable an element disabled using el.disabled.
Affects all connected instances.
```python
await wui.el_enable(['#some_input', '#some_button'])
```
- `selectors(list[str])` -- querySelectorAll selectors for targeting.
### `el_set_attribute`
Use el.setAttribute on one or more targets.
Affects all connected instances.
```python
await wui.el_set_attribute(['#my_element'], 'data-something', 'the value')
```
- `selectors(list[str])` -- querySelectorAll selectors for targeting.
- `name(str)` -- name of the attribute.
- `content(str)` -- value of the attribute.
### `el_set_html_templ`
Evaluate a Jinja2 template string and set the el.innerHTML of one or more targets.
Affects all connected instances.
```python
await wui.el_set_html_templ(
['#my_element'],
'<div>{{ content }}</div>',
content='Stuff',
)
```
- `selectors(list[str])` -- querySelectorAll selectors for targeting.
- `content(str)` -- a string containing a Jinja2 template.
### `el_set_html_unsafe`
Directly set the el.innerHTML of one or more targets.
Affects all connected instances.
```python
await wui.el_set_html_unsafe(['#my_element'], '<div>Stuff</div>')
```
- `selectors(list[str])` -- querySelectorAll selectors for targeting.
- `content(str)` -- a string containing html.
### `el_set_style`
Set a property on el.style on one or more targets.
Affects all connected instances.
```python
await wui.el_set_style(['#my_element'], 'backgroundColor', 'lightgray')
```
- `selectors(list[str])` -- querySelectorAll selectors for targeting.
- `name(str)` -- name of the style attribute (camelCase).
- `content(str)` -- value of the attribute.
### `el_set_text`
Directly set the el.innerText of one or more targets.
Affects all connected instances.
```python
await wui.el_set_text(['#my_element'], 'Hello there')
```
- `selectors(list[str])` -- querySelectorAll selectors for targeting.
- `content(str)` -- a string containing raw text.
### `el_set_value`
Set the el.value of one or more targets.
Affects all connected instances.
```python
await wui.el_set_value(['#my_input'], 'Hello')
```
- `selectors(list[str])` -- querySelectorAll selectors for targeting.
- `content(str)` -- a string containing any text.
### `pg_eval`
Eval a js string on the frontend.
Affects all connected instances.
```python
await wui.pg_eval('call_some_javascript()')
```
- `content(str)` -- the js string
### `pg_set_title`
Set the title of the page dynamically.
Affects all connected instances.
```python
await wui.pg_set_title('A New Title')
```
- `content(str)` -- the page title
### `run`
Run the BrutalPyWebUI app at host on port.
```python
await wui.run(host="localhost", port=7865)
```
- `host(str)` -- hostname.
- `port(int)` -- port number.
## JavaScript reference
### `_wuiEvent`
Send an event to your `event_handler` on the backend.
```javascript
_wuiEvent('my_event', ['some', 'data'])
```
- `name(string)` -- name of your event
- `data(any)` -- json-compatible object to send with your event
### `_wuiVal`
Returns `el.value` of an element.
```javascript
_wuiVal('#my_input')
```
- `selector(string)` -- querySelector selector
### `_wuiChecked`
Returns `el.checked` of an element.
```javascript
_wuiChecked('#my_input')
```
- `selector(string)` -- querySelector selector
### `_wuiSelected`
Returns `el.selected` of an element.
```javascript
_wuiSelected('#my_input')
```
- `selector(string)` -- querySelector selector
Raw data
{
"_id": null,
"home_page": "https://github.com/zenoverflow/brutalpywebui",
"name": "brutalpywebui",
"maintainer": "",
"docs_url": null,
"requires_python": ">=3.11,<4.0",
"maintainer_email": "",
"keywords": "web,ui,frontend,backend,asyncio,websocket,html5,css,javascript",
"author": "Zen",
"author_email": "zen@zenoverflow.com",
"download_url": "https://files.pythonhosted.org/packages/46/5e/4a54a7d0add6da00a0e2501ccd20eb849c1c386bff41147d1ee5c45e2fa7/brutalpywebui-0.1.0.tar.gz",
"platform": null,
"description": "\n## Intro\nBrutalPyWebUI is a brutalist async Python framework for building\nutilitarian desktop applications that require a tried-and-true\nweb-based cross-platform interface with realtime capabilities and no hassle.\n\n## Simple and focused\nBrutalPyWebUI exposes a basic web interface bound to a host and port of\nyour choice, and provides you a selection of very basic utility\nfunctions to update the DOM on the frontend from your Python backend,\nas well as a few global javascript functions for quickly\ngrabbing data from the interface on the frontend and sending\nit to the backend.\n\n## Light on dependencies\nBrutalPyWebUI depends only on:\n- [hypercorn (the ASGI server)](https://github.com/pgjones/hypercorn),\n- [Quart (a Flask replacement for asyncio)](https://github.com/pallets/quart)\n\n## Light on assets\nBrutalPyWebUI bundles only:\n- [a default favicon (the Pyton logo)](https://www.favicon.cc/?action=icon&file_id=831343)\n- [a default font (JetBrains Mono Regular)](https://github.com/JetBrains/JetBrainsMono)\n- [a default css reset (Normalize.css)](https://github.com/necolas/normalize.css)\n\nNote that you can disable all three separately.\n\n## Flexible UI design\nThere are three main approaches in regards to actually\nbuilding the user interface:\n\n### Utilitarian\nWrite most of your logic in Python and use the UI\nas a barebones interface that sends events by calling\nthe special global `_wuiEvent()` function from a script\nor an onclick attribute, etc., and gets updated\non-demand, from your Python code, by calling any\nof the `wui.el_*()` or `wui.pg_*()` utility functions.\n\n### Embrace Modernity\nUse Python-land as more of a complementary backend and\ninject heavier javascript and css that can contain\nanything from logic and styling for basic web components\nto the bundled code of a full-fledged React app. Note that\nany js you inject using `base_js` in the constructor\nis injected after the definitions for the global `_wui*()` functions\nso they will not be overriden and you can call them normally\nfrom within your app. You could also just use `asset_handler`\nto serve your scripts along with other files.\n\n### Anything in the middle / anything you can think of\nSelf-explanatory.\n\nThe idea is to give the developer a sturdy platform to\nbuild upon, expanding freely as needed, without\nhighly specific enforced patterns.\n\n\n## Installation\n```bash\npip install brutalpywebui\n```\n\n## Basic usage\n```python\nimport typing as t\nfrom brutalpywebui import BrutalPyWebUI\n\n\nexample_html = '''\n<input id=\"inp_regular_txt\" value=\"Updated value\" />\n<button onclick=\"_wuiEvent('btn_press', _wuiVal('#inp_regular_txt'))\">{{ button_text }}</button>\n<div>Result: <span id=\"txt_result\">Old value</span></div>\n<div>Ticker: <span id=\"txt_ticker\">{{ ticker_text }}</span></div>\n'''\n\nexample_css = '''\nbody {\n display: flex;\n flex-direction: column;\n align-items: center;\n padding: 30px 20px;\n}\n\nbody > * {\n width: 100%;\n height: 24px;\n max-width: 300px;\n margin-bottom: 10px;\n padding: 0;\n border: none;\n outline: none;\n box-sizing: border-box;\n}\n\nbody > input {\n border: 1px solid black;\n padding: 0 3px;\n}\n'''\n\nwui: BrutalPyWebUI | None = None\n\nticker = 0\n\n\nasync def on_background():\n global ticker\n\n ticker += 1\n await wui.el_set_text([\"#txt_ticker\"], str(ticker))\n\n\nasync def on_event(event: str, data: t.Any):\n match event:\n case \"btn_press\":\n await wui.el_set_text([\"#txt_result\"], data)\n\n case _:\n pass\n\n\nasync def on_init():\n # this uses Jinja2 templates\n await wui.el_set_html_templ(\n [\"body\"],\n example_html,\n button_text=\"Press me!\",\n ticker_text=str(ticker),\n )\n\n\nwui = BrutalPyWebUI(\n page_title=\"MyApp\",\n init_handler=on_init,\n event_handler=on_event,\n background_handler=on_background,\n background_interval=5.2, # seconds\n base_css=example_css,\n)\n\n# these are the default host and port\n# specified here for the sake of the example\nwui.run(host=\"localhost\", port=7865)\n\n```\n\n## Technical note\nThe run() method uses asyncio.run() and is meant\nto be run on the main thread. The app will not be\nserved until run() is called.\n\n## Python reference\n\n\n### `__init__`\n\n\n```python\nasync def on_init():\n pass\n\nasync def on_event(name: str, data: t.Any):\n pass\n\nasync def on_asset(asset_name: str):\n # served at yourhost:port/assets/<asset_name>\n asset_bytes, content_type = mock_read_asset(asset_name)\n return asset_bytes, content_type\n\nwui = BrutalPyWebUI(\n page_title=\"MyApp\",\n init_handler=on_init,\n event_handler=on_event,\n asset_handler=on_asset,\n background_handler=on_background,\n background_interval=5.2, # seconds\n base_css=lambda: mock_read_asset(\"bundle.css\") # or pass str directly,\n base_js=lambda: mock_read_asset(\"bundle.js\") # or pass str directly,\n inject_python_favicon=True,\n inject_jetbrains_font=True,\n inject_normalize_css=True,\n page_websocket_use_tls=False,\n page_lang=lambda: \"en\", # or pass str directly,\n page_encoding=lambda: \"UTF-8\", # or pass str directly,\n page_viewport=lambda: \"width=device-width, initial-scale=1.0\", # or pass str directly,\n debug=False, # print debug logs where applicable\n)\n```\n\n- `page_title(str|()->str)` -- title of the page.\n- `init_handler(async()->None)` -- coroutine to run on init.\n- `event_handler(async(str,Any)->None)` -- coroutine to handle your custom events\nfrom the frontend.\n- `asset_handler(async(str)->(asset_content(Any), content_type(str)))` -- coroutine\nto handle serving your custom assets for the frontend.\n- `background_handler(async()->None)` -- coroutine called periodically in the background\ncontrolled by the background_interval arg.\n- `background_interval(float)` -- how often (in seconds with subsecond precision)\nto call the background_handler.\n- `base_css(str|()->str)` -- css string to inject in the main css file\nfor the frontend.\n- `base_js(str|()->str)` -- js string to inject into the main js file\nfor the frontend.\n- `inject_python_favicon(bool)` -- whether to use a default favicon.\n- `inject_jetbrains_font(bool)` -- whether to use the JetBrains Mono Regular font.\n- `inject_normalize_css(bool)` -- whether to inject NormalizeCSS into the main css file\nfor the frontend.\n- `page_websocket_use_tls(bool)` -- whether to use wss instead of ws for the websocket\nconnection on the frontend.\n- `page_lang(str)` -- the lang attribute for the html tag.\n- `page_encoding(str)` -- the encoding of the page, normally you should not change this.\n- `page_viewport(str)` -- the value for the viewport meta tag, normally you should not\nchange this unless having responsive issues with CSS.\n- `debug(bool)` -- whether to print debug logs where applicable.\n\n\n### `el_append_text`\n\n\nAppend text to the current el.innerText of one or more targets.\nAffects all connected instances.\n\n```python\nawait wui.el_set_text(['#my_element'], 'Hello')\nawait wui.el_append_text(['#my_element'], ' there')\n```\n\n- `selectors(list[str])` -- querySelectorAll selectors for targeting.\n- `content(str)` -- a string containing any text.\n\n\n### `el_append_value`\n\n\nAppend text to the current el.value of one or more targets.\nAffects all connected instances.\n\n```python\nawait wui.el_set_value(['#my_input'], 'Hello')\nawait wui.el_append_value(['#my_input'], ' there')\n```\n\n- `selectors(list[str])` -- querySelectorAll selectors for targeting.\n- `content(str)` -- a string containing any text.\n\n\n### `el_class_add`\n\n\nAdd a class to el.classList on one or more targets.\nAffects all connected instances.\n\n```python\nawait wui.el_class_add(['#my_element'], 'my_custom_class')\n```\n\n- `selectors(list[str])` -- querySelectorAll selectors for targeting.\n- `name(str)` -- name of the class.\n\n\n### `el_class_remove`\n\n\nRemove a class from el.classList on one or more targets.\nAffects all connected instances.\n\n```python\nawait wui.el_class_remove(['#my_element'], 'my_custom_class')\n```\n\n- `selectors(list[str])` -- querySelectorAll selectors for targeting.\n- `name(str)` -- name of the class.\n\n\n### `el_disable`\n\n\nDisable an element using el.disabled.\nAffects all connected instances.\n\n```python\nawait wui.el_disable(['#some_input', '#some_button'])\n```\n\n- `selectors(list[str])` -- querySelectorAll selectors for targeting.\n\n\n### `el_enable`\n\n\nEnable an element disabled using el.disabled.\nAffects all connected instances.\n\n```python\nawait wui.el_enable(['#some_input', '#some_button'])\n```\n\n- `selectors(list[str])` -- querySelectorAll selectors for targeting.\n\n\n### `el_set_attribute`\n\n\nUse el.setAttribute on one or more targets.\nAffects all connected instances.\n\n```python\nawait wui.el_set_attribute(['#my_element'], 'data-something', 'the value')\n```\n\n- `selectors(list[str])` -- querySelectorAll selectors for targeting.\n- `name(str)` -- name of the attribute.\n- `content(str)` -- value of the attribute.\n\n\n### `el_set_html_templ`\n\n\nEvaluate a Jinja2 template string and set the el.innerHTML of one or more targets.\nAffects all connected instances.\n\n```python\nawait wui.el_set_html_templ(\n ['#my_element'],\n '<div>{{ content }}</div>',\n content='Stuff',\n)\n```\n\n- `selectors(list[str])` -- querySelectorAll selectors for targeting.\n- `content(str)` -- a string containing a Jinja2 template.\n\n\n### `el_set_html_unsafe`\n\n\nDirectly set the el.innerHTML of one or more targets.\nAffects all connected instances.\n\n```python\nawait wui.el_set_html_unsafe(['#my_element'], '<div>Stuff</div>')\n```\n\n- `selectors(list[str])` -- querySelectorAll selectors for targeting.\n- `content(str)` -- a string containing html.\n\n\n### `el_set_style`\n\n\nSet a property on el.style on one or more targets.\nAffects all connected instances.\n\n```python\nawait wui.el_set_style(['#my_element'], 'backgroundColor', 'lightgray')\n```\n\n- `selectors(list[str])` -- querySelectorAll selectors for targeting.\n- `name(str)` -- name of the style attribute (camelCase).\n- `content(str)` -- value of the attribute.\n\n\n### `el_set_text`\n\n\nDirectly set the el.innerText of one or more targets.\nAffects all connected instances.\n\n```python\nawait wui.el_set_text(['#my_element'], 'Hello there')\n```\n\n- `selectors(list[str])` -- querySelectorAll selectors for targeting.\n- `content(str)` -- a string containing raw text.\n\n\n### `el_set_value`\n\n\nSet the el.value of one or more targets.\nAffects all connected instances.\n\n```python\nawait wui.el_set_value(['#my_input'], 'Hello')\n```\n\n- `selectors(list[str])` -- querySelectorAll selectors for targeting.\n- `content(str)` -- a string containing any text.\n\n\n### `pg_eval`\n\n\nEval a js string on the frontend.\nAffects all connected instances.\n\n```python\nawait wui.pg_eval('call_some_javascript()')\n```\n\n- `content(str)` -- the js string\n\n\n### `pg_set_title`\n\n\nSet the title of the page dynamically.\nAffects all connected instances.\n\n```python\nawait wui.pg_set_title('A New Title')\n```\n\n- `content(str)` -- the page title\n\n\n### `run`\n\n\nRun the BrutalPyWebUI app at host on port.\n\n```python\nawait wui.run(host=\"localhost\", port=7865)\n```\n\n- `host(str)` -- hostname.\n- `port(int)` -- port number.\n\n\n## JavaScript reference\n\n### `_wuiEvent`\n\nSend an event to your `event_handler` on the backend.\n\n```javascript\n_wuiEvent('my_event', ['some', 'data'])\n```\n\n- `name(string)` -- name of your event\n- `data(any)` -- json-compatible object to send with your event\n\n### `_wuiVal`\n\nReturns `el.value` of an element.\n\n```javascript\n_wuiVal('#my_input')\n```\n\n- `selector(string)` -- querySelector selector\n\n### `_wuiChecked`\n\nReturns `el.checked` of an element.\n\n```javascript\n_wuiChecked('#my_input')\n```\n\n- `selector(string)` -- querySelector selector\n\n### `_wuiSelected`\n\nReturns `el.selected` of an element.\n\n```javascript\n_wuiSelected('#my_input')\n```\n\n- `selector(string)` -- querySelector selector\n",
"bugtrack_url": null,
"license": "MIT",
"summary": "Brutalist realtime web-based UI for hassle-free desktop apps.",
"version": "0.1.0",
"project_urls": {
"Homepage": "https://github.com/zenoverflow/brutalpywebui",
"Repository": "https://github.com/zenoverflow/brutalpywebui"
},
"split_keywords": [
"web",
"ui",
"frontend",
"backend",
"asyncio",
"websocket",
"html5",
"css",
"javascript"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "fcccc36f9d88f02831751ce4af99d3bb15254ce70a347be39f832b15e996b084",
"md5": "c6b9ddde7e657f6ed42889a3fa038917",
"sha256": "74039aad9c2403486cc281cd2bebe207ae13fb9c6d88a0d6a3b95576c203c42c"
},
"downloads": -1,
"filename": "brutalpywebui-0.1.0-py3-none-any.whl",
"has_sig": false,
"md5_digest": "c6b9ddde7e657f6ed42889a3fa038917",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.11,<4.0",
"size": 185512,
"upload_time": "2024-02-06T21:35:56",
"upload_time_iso_8601": "2024-02-06T21:35:56.440690Z",
"url": "https://files.pythonhosted.org/packages/fc/cc/c36f9d88f02831751ce4af99d3bb15254ce70a347be39f832b15e996b084/brutalpywebui-0.1.0-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "465e4a54a7d0add6da00a0e2501ccd20eb849c1c386bff41147d1ee5c45e2fa7",
"md5": "e414e544dd16ad0a926d9582e360dd31",
"sha256": "5ddec54e3246ea4b0ceea5c4764c6f7e5d2140c69422f8db9cc94d6951dde00d"
},
"downloads": -1,
"filename": "brutalpywebui-0.1.0.tar.gz",
"has_sig": false,
"md5_digest": "e414e544dd16ad0a926d9582e360dd31",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.11,<4.0",
"size": 185329,
"upload_time": "2024-02-06T21:35:59",
"upload_time_iso_8601": "2024-02-06T21:35:59.157546Z",
"url": "https://files.pythonhosted.org/packages/46/5e/4a54a7d0add6da00a0e2501ccd20eb849c1c386bff41147d1ee5c45e2fa7/brutalpywebui-0.1.0.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2024-02-06 21:35:59",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "zenoverflow",
"github_project": "brutalpywebui",
"travis_ci": false,
"coveralls": false,
"github_actions": false,
"lcname": "brutalpywebui"
}