Skip to content

Commit

Permalink
make render_template method async, update readme and examples
Browse files Browse the repository at this point in the history
  • Loading branch information
patx committed Jan 28, 2025
1 parent 5e5d49f commit cbe13f9
Show file tree
Hide file tree
Showing 13 changed files with 53 additions and 621 deletions.
21 changes: 14 additions & 7 deletions MicroPie.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,14 @@
OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""

import time
import uuid
import asyncio
import inspect
import os
import mimetypes
from urllib.parse import parse_qs
import os
import time
from typing import Optional, Dict, Any, Union, Tuple, List
from urllib.parse import parse_qs
import uuid

try:
from jinja2 import Environment, FileSystemLoader
Expand Down Expand Up @@ -366,8 +366,15 @@ def redirect(self, location: str) -> Tuple[int, str]:
),
)

def render_template(self, name: str, **kwargs: Any) -> str:
async def render_template(self, name: str, **kwargs: Any) -> str:
"""
Async-compatible template rendering using Jinja2.
"""
if not JINJA_INSTALLED:
raise ImportError("Jinja2 is not installed.")
return self.env.get_template(name).render(kwargs)

def render_sync():
return self.env.get_template(name).render(kwargs)

return await asyncio.get_event_loop().run_in_executor(None, render_sync)

35 changes: 16 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,46 +59,43 @@ Access your app at [http://127.0.0.1:8000](http://127.0.0.1:8000).

## **Core Features**

### **1. Flexible Routing**
### **1. Flexible HTTP Routing**
MicroPie automatically maps URLs to methods within your `Server` class. Routes can be defined as either synchronous or asynchronous functions, offering good flexibility.

#### **Basic Routing**
For GET requests, pass data through query strings or URL path segments, automatically mapped to method arguments.
```python
class MyApp(Server):
def hello(self):
return "Hello, world!"

async def async_hello(self):
return "Hello from an async route!"
def async greet(self, name="Guest"):
return f"Hello, {name}!"
```
**Access:**
- Sync route: [http://127.0.0.1:8000/hello](http://127.0.0.1:8000/hello)
- Async route: [http://127.0.0.1:8000/async_hello](http://127.0.0.1:8000/async_hello)
- [http://127.0.0.1:8000/greet?name=Alice](http://127.0.0.1:8000/greet?name=Alice) returns `Hello, Alice!`
- [http://127.0.0.1:8000/greet/Alice](http://127.0.0.1:8000/greet/Alice) returns `Hello, Alice!`

### **2. Query and Path Parameters**
Pass data through query strings or URL path segments, automatically mapped to method arguments.
MicroPie also supports handling form data submitted via HTTP POST requests. Form data is automatically mapped to method arguments. It is able to handle default values and raw POST data:
```python
class MyApp(Server):
def async greet(self, name="Guest"):
return f"Hello, {name}!"
def submit_default_values(self, username="Anonymous"):
return f"Form submitted by: {username}"

def submit_catch_all(self):
username = self.body_params.get('username', ['Anonymous'])[0]
return f"Submitted by: {username}"
```

**Access:**
- [http://127.0.0.1:8000/greet?name=Alice](http://127.0.0.1:8000/greet?name=Alice) returns `Hello, Alice!`
- [http://127.0.0.1:8000/greet/Alice](http://127.0.0.1:8000/greet/Alice) returns `Hello, Alice!`

### **3. Real-Time Communication with Socket.IO**
Because of its designed simplicity, MicroPie does not handle WebSockets out of the box. While the underlying ASGI interface can theoretically handle WebSocket connections, MicroPie’s routing and request-handling logic is designed primarily for HTTP. While MicroPie does not natively support WebSockets, you can easily integrate dedicated Websockets libraries like **Socket.IO** alongside Uvicorn to handle real-time, bidirectional communication. Check out [examples/socketio](https://github.com/patx/micropie/tree/main/examples/socketio) to see this in action.


### **4. Jinja2 Template Rendering**
Dynamic HTML generation is supported via Jinja2.
Dynamic HTML generation is supported via Jinja2. This happens asynchronously using Pythons `asyncio` library, so make sure to use the `async` and `await` with this method.

#### **`app.py`**
```python
class MyApp(Server):
def index(self):
return self.render_template("index.html", title="Welcome", message="Hello from MicroPie!")
async def index(self):
return await self.render_template("index.html", title="Welcome", message="Hello from MicroPie!")
```

#### **`templates/index.html`**
Expand Down
91 changes: 0 additions & 91 deletions examples/chatroom/app.py

This file was deleted.

4 changes: 2 additions & 2 deletions examples/pastebin/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,14 @@ async def index(self):
db.set(pid, escape(paste_content))
db.save()
return self.redirect(f'/paste/{pid}')
return self.render_template('index.html')
return await self.render_template('index.html')

async def paste(self, paste_id, delete=None):
if delete == 'delete':
db.remove(paste_id)
db.save()
return self.redirect('/')
return self.render_template(
return await self.render_template(
'paste.html',
paste_id=paste_id,
paste_content=db.get(paste_id)
Expand Down
6 changes: 3 additions & 3 deletions examples/socketio/webcam.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
# MicroPie Server with integrated Socket.IO
class MyApp(Server):
async def index(self):
return self.render_template("index_stream.html")
return await self.render_template("index_stream.html")

async def submit(self, username: str, action: str):
if username:
Expand All @@ -20,10 +20,10 @@ async def submit(self, username: str, action: str):
return self.redirect("/")

async def stream(self, username: str):
return self.render_template("stream.html", username=username) if username in active_users else self.redirect("/")
return await self.render_template("stream.html", username=username) if username in active_users else self.redirect("/")

async def watch(self, username: str):
return self.render_template("watch.html", username=username) if username in active_users else self.redirect("/")
return await self.render_template("watch.html", username=username) if username in active_users else self.redirect("/")

# Socket.IO event handlers
@sio.event
Expand Down
51 changes: 0 additions & 51 deletions examples/streaming/livestream.py

This file was deleted.

87 changes: 0 additions & 87 deletions examples/streaming/video1.py

This file was deleted.

Loading

0 comments on commit cbe13f9

Please sign in to comment.