SlimeWeb 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.
- slimeweb-0.1.0/.gitignore +5 -0
- slimeweb-0.1.0/PKG-INFO +154 -0
- slimeweb-0.1.0/README.md +146 -0
- slimeweb-0.1.0/pyproject.toml +22 -0
- slimeweb-0.1.0/slimeweb/__init__.py +1 -0
- slimeweb-0.1.0/slimeweb/cli.py +124 -0
- slimeweb-0.1.0/slimeweb/slime.py +197 -0
- slimeweb-0.1.0/slimeweb/web/__init__.py +5 -0
- slimeweb-0.1.0/slimeweb/web/web.cp314t-win_amd64.pyd +0 -0
- slimeweb-0.1.0/slimeweb/web/web.cpython-314t-x86_64-linux-gnu.so +0 -0
slimeweb-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: SlimeWeb
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A Simple fast lightweight webframework lib for python
|
|
5
|
+
Author-email: "S.ABILASH" <abinix01@gmail.com>
|
|
6
|
+
Requires-Python: >=3.12
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
|
|
9
|
+
# Slime – A Rust + Python Hybrid Web Framework
|
|
10
|
+
|
|
11
|
+
Slime is a high-performance web framework that combines Rust and python
|
|
12
|
+
|
|
13
|
+
It is designed for developers who want Python developer experience with a Rust-powered server core.
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
- pip install slimeweb
|
|
19
|
+
- slime new <Project Name>
|
|
20
|
+
- cd <Project Name>
|
|
21
|
+
- slime run <Project Name>
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## Features
|
|
28
|
+
|
|
29
|
+
- Python handler functions
|
|
30
|
+
- Multiple worker pool model
|
|
31
|
+
- Synchronous handler
|
|
32
|
+
- Multipart form support
|
|
33
|
+
- File uploads
|
|
34
|
+
- Streaming Response
|
|
35
|
+
- Cookie signing
|
|
36
|
+
- Custom headers
|
|
37
|
+
- JSON / HTML / raw responses
|
|
38
|
+
- Templates rendering with context
|
|
39
|
+
- Hot reload of templates in dev mode
|
|
40
|
+
- WebSocket
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
## Basic Application
|
|
47
|
+
|
|
48
|
+
```python
|
|
49
|
+
@app.route(path="/", method="GET")
|
|
50
|
+
def index(req, resp):
|
|
51
|
+
return resp.plain("Hello from Slime")
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
## Request Body
|
|
56
|
+
|
|
57
|
+
```python
|
|
58
|
+
@app.route(path="/test", method="POST")
|
|
59
|
+
def hello(req, resp):
|
|
60
|
+
print("query", req.query)
|
|
61
|
+
print("params", req.params)
|
|
62
|
+
print("body", req.body)
|
|
63
|
+
print("json", req.json)
|
|
64
|
+
print("form", req.form)
|
|
65
|
+
print("text", req.text)
|
|
66
|
+
print("bytes", req.bytes)
|
|
67
|
+
print("file",req.file)
|
|
68
|
+
return resp.json({"status": "ok"})
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
### File Upload
|
|
74
|
+
```python
|
|
75
|
+
@app.route(path="/test", method="POST")
|
|
76
|
+
def hello(req, resp):
|
|
77
|
+
file = req.file[0] # use can upload multiple files
|
|
78
|
+
print(file.filename)
|
|
79
|
+
print(file.content_type)
|
|
80
|
+
print(file.file_path)
|
|
81
|
+
print(file.file_size)
|
|
82
|
+
print(file.extension)
|
|
83
|
+
file.save(f"testing_file.{file.extension}")
|
|
84
|
+
return resp.json({"status": "ok"})
|
|
85
|
+
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
### Template Render
|
|
90
|
+
|
|
91
|
+
```python
|
|
92
|
+
@app.route(path="/", method="GET")
|
|
93
|
+
def land(req, resp):
|
|
94
|
+
html = req.render("hello.html", **{"name": "abilash", "slimeVersion": "0.0.1"})
|
|
95
|
+
return resp.html(html)
|
|
96
|
+
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### Middleware
|
|
100
|
+
|
|
101
|
+
```python
|
|
102
|
+
#NOTE: middleware should be declared after declaring route handler
|
|
103
|
+
# LifeCycle handler =>
|
|
104
|
+
# middle before request -> router handler -> middle after request
|
|
105
|
+
|
|
106
|
+
@app.middle_after(path="/", method="GET")
|
|
107
|
+
def land_after(req, resp):
|
|
108
|
+
resp.set_header("BEFORE", "Request")
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
@app.middle_before(path="/", method="GET")
|
|
112
|
+
def land_before(req, resp):
|
|
113
|
+
resp.set_header("AFTER", "REQUEST")
|
|
114
|
+
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Streaming
|
|
118
|
+
|
|
119
|
+
```python
|
|
120
|
+
@app.route(path="/stream", method="GET", stream="text/plain")
|
|
121
|
+
def stream_me(req, resp):
|
|
122
|
+
resp.start_stream()
|
|
123
|
+
for i in range(5):
|
|
124
|
+
resp.send(i)
|
|
125
|
+
resp.close()
|
|
126
|
+
|
|
127
|
+
# OR u can use @app.stream
|
|
128
|
+
|
|
129
|
+
@app.stream(path="/stream", method="GET", content="text/plain")
|
|
130
|
+
def stream_me(req, resp):
|
|
131
|
+
resp.start_stream()
|
|
132
|
+
for i in range(5):
|
|
133
|
+
resp.send(i)
|
|
134
|
+
resp.close()
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
### WebSocket
|
|
140
|
+
```python
|
|
141
|
+
@app.websocket(path="/chat", method="GET")
|
|
142
|
+
def chatty(req, resp):
|
|
143
|
+
|
|
144
|
+
def read_me(msg):
|
|
145
|
+
if not resp.is_closed():
|
|
146
|
+
resp.send(msg)
|
|
147
|
+
|
|
148
|
+
resp.on_message(read_me)
|
|
149
|
+
|
|
150
|
+
def close_me():
|
|
151
|
+
pass
|
|
152
|
+
|
|
153
|
+
resp.on_close(close_me)
|
|
154
|
+
```
|
slimeweb-0.1.0/README.md
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# Slime – A Rust + Python Hybrid Web Framework
|
|
2
|
+
|
|
3
|
+
Slime is a high-performance web framework that combines Rust and python
|
|
4
|
+
|
|
5
|
+
It is designed for developers who want Python developer experience with a Rust-powered server core.
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
## Installation
|
|
9
|
+
|
|
10
|
+
- pip install slimeweb
|
|
11
|
+
- slime new <Project Name>
|
|
12
|
+
- cd <Project Name>
|
|
13
|
+
- slime run <Project Name>
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Features
|
|
20
|
+
|
|
21
|
+
- Python handler functions
|
|
22
|
+
- Multiple worker pool model
|
|
23
|
+
- Synchronous handler
|
|
24
|
+
- Multipart form support
|
|
25
|
+
- File uploads
|
|
26
|
+
- Streaming Response
|
|
27
|
+
- Cookie signing
|
|
28
|
+
- Custom headers
|
|
29
|
+
- JSON / HTML / raw responses
|
|
30
|
+
- Templates rendering with context
|
|
31
|
+
- Hot reload of templates in dev mode
|
|
32
|
+
- WebSocket
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
## Basic Application
|
|
39
|
+
|
|
40
|
+
```python
|
|
41
|
+
@app.route(path="/", method="GET")
|
|
42
|
+
def index(req, resp):
|
|
43
|
+
return resp.plain("Hello from Slime")
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
## Request Body
|
|
48
|
+
|
|
49
|
+
```python
|
|
50
|
+
@app.route(path="/test", method="POST")
|
|
51
|
+
def hello(req, resp):
|
|
52
|
+
print("query", req.query)
|
|
53
|
+
print("params", req.params)
|
|
54
|
+
print("body", req.body)
|
|
55
|
+
print("json", req.json)
|
|
56
|
+
print("form", req.form)
|
|
57
|
+
print("text", req.text)
|
|
58
|
+
print("bytes", req.bytes)
|
|
59
|
+
print("file",req.file)
|
|
60
|
+
return resp.json({"status": "ok"})
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
### File Upload
|
|
66
|
+
```python
|
|
67
|
+
@app.route(path="/test", method="POST")
|
|
68
|
+
def hello(req, resp):
|
|
69
|
+
file = req.file[0] # use can upload multiple files
|
|
70
|
+
print(file.filename)
|
|
71
|
+
print(file.content_type)
|
|
72
|
+
print(file.file_path)
|
|
73
|
+
print(file.file_size)
|
|
74
|
+
print(file.extension)
|
|
75
|
+
file.save(f"testing_file.{file.extension}")
|
|
76
|
+
return resp.json({"status": "ok"})
|
|
77
|
+
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
### Template Render
|
|
82
|
+
|
|
83
|
+
```python
|
|
84
|
+
@app.route(path="/", method="GET")
|
|
85
|
+
def land(req, resp):
|
|
86
|
+
html = req.render("hello.html", **{"name": "abilash", "slimeVersion": "0.0.1"})
|
|
87
|
+
return resp.html(html)
|
|
88
|
+
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Middleware
|
|
92
|
+
|
|
93
|
+
```python
|
|
94
|
+
#NOTE: middleware should be declared after declaring route handler
|
|
95
|
+
# LifeCycle handler =>
|
|
96
|
+
# middle before request -> router handler -> middle after request
|
|
97
|
+
|
|
98
|
+
@app.middle_after(path="/", method="GET")
|
|
99
|
+
def land_after(req, resp):
|
|
100
|
+
resp.set_header("BEFORE", "Request")
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
@app.middle_before(path="/", method="GET")
|
|
104
|
+
def land_before(req, resp):
|
|
105
|
+
resp.set_header("AFTER", "REQUEST")
|
|
106
|
+
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### Streaming
|
|
110
|
+
|
|
111
|
+
```python
|
|
112
|
+
@app.route(path="/stream", method="GET", stream="text/plain")
|
|
113
|
+
def stream_me(req, resp):
|
|
114
|
+
resp.start_stream()
|
|
115
|
+
for i in range(5):
|
|
116
|
+
resp.send(i)
|
|
117
|
+
resp.close()
|
|
118
|
+
|
|
119
|
+
# OR u can use @app.stream
|
|
120
|
+
|
|
121
|
+
@app.stream(path="/stream", method="GET", content="text/plain")
|
|
122
|
+
def stream_me(req, resp):
|
|
123
|
+
resp.start_stream()
|
|
124
|
+
for i in range(5):
|
|
125
|
+
resp.send(i)
|
|
126
|
+
resp.close()
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
### WebSocket
|
|
132
|
+
```python
|
|
133
|
+
@app.websocket(path="/chat", method="GET")
|
|
134
|
+
def chatty(req, resp):
|
|
135
|
+
|
|
136
|
+
def read_me(msg):
|
|
137
|
+
if not resp.is_closed():
|
|
138
|
+
resp.send(msg)
|
|
139
|
+
|
|
140
|
+
resp.on_message(read_me)
|
|
141
|
+
|
|
142
|
+
def close_me():
|
|
143
|
+
pass
|
|
144
|
+
|
|
145
|
+
resp.on_close(close_me)
|
|
146
|
+
```
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "SlimeWeb"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "A Simple fast lightweight webframework lib for python"
|
|
5
|
+
authors = [{ name= "S.ABILASH", email="abinix01@gmail.com" }]
|
|
6
|
+
readme = "README.md"
|
|
7
|
+
requires-python = ">=3.12"
|
|
8
|
+
|
|
9
|
+
[project.scripts]
|
|
10
|
+
slime = "slimeweb.cli:main"
|
|
11
|
+
|
|
12
|
+
[build-system]
|
|
13
|
+
requires = ["hatchling"]
|
|
14
|
+
build-backend = "hatchling.build"
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
[tool.hatch.build]
|
|
18
|
+
packages = ["slimeweb"]
|
|
19
|
+
artifacts = [
|
|
20
|
+
"slimeweb/**/*.so",
|
|
21
|
+
"slimeweb/**/*.pyd"
|
|
22
|
+
]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .slime import Slime
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
# AUTHOR: S.ABILASH
|
|
2
|
+
# Email: abinix01@gmail.com
|
|
3
|
+
|
|
4
|
+
import os
|
|
5
|
+
import shutil
|
|
6
|
+
import subprocess as sp
|
|
7
|
+
import sys
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def create_project(name: str):
|
|
12
|
+
if shutil.which("uv") is None:
|
|
13
|
+
print("\n[*] uv is not found installing...")
|
|
14
|
+
command = [sys.executable, "-m", "pip", "install", "uv"]
|
|
15
|
+
sp.run(command, check=True)
|
|
16
|
+
|
|
17
|
+
print(f"\n[*] Creating project {name}")
|
|
18
|
+
root = Path.cwd() / name
|
|
19
|
+
|
|
20
|
+
static_path = root / "static"
|
|
21
|
+
template_path = root / "templates"
|
|
22
|
+
|
|
23
|
+
static_path.mkdir(parents=True, exist_ok=True)
|
|
24
|
+
template_path.mkdir(parents=True, exist_ok=True)
|
|
25
|
+
|
|
26
|
+
script_path = root / "main.py"
|
|
27
|
+
|
|
28
|
+
code = """
|
|
29
|
+
from slimeweb import Slime
|
|
30
|
+
|
|
31
|
+
app = Slime(__file__)
|
|
32
|
+
|
|
33
|
+
@app.route(path="/", method="GET")
|
|
34
|
+
def home(req, resp):
|
|
35
|
+
return resp.plain("Hello World from slime")
|
|
36
|
+
|
|
37
|
+
if __name__ == "__main__":
|
|
38
|
+
app.serve(dev=True)
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
script_path.write_text(code)
|
|
42
|
+
|
|
43
|
+
print("[*] Creating an env")
|
|
44
|
+
if not (root / ".venv").exists():
|
|
45
|
+
os.chdir(root)
|
|
46
|
+
sp.run(
|
|
47
|
+
["uv", "venv", "--python", "python3.14t"],
|
|
48
|
+
check=True,
|
|
49
|
+
stdout=sp.DEVNULL,
|
|
50
|
+
stderr=sp.DEVNULL,
|
|
51
|
+
)
|
|
52
|
+
sp.run(
|
|
53
|
+
["uv", "python", "pin", "python3.14t"],
|
|
54
|
+
check=True,
|
|
55
|
+
stdout=sp.DEVNULL,
|
|
56
|
+
stderr=sp.DEVNULL,
|
|
57
|
+
)
|
|
58
|
+
sp.run(["uv", "init"])
|
|
59
|
+
sp.run(["uv", "add", "slimeweb"], check=True)
|
|
60
|
+
print(f"\n\n[*] Project '{name}' created 🎉🎉🎉")
|
|
61
|
+
print(f"[*] cd {name} ")
|
|
62
|
+
|
|
63
|
+
print("[*] slime run main")
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def run_project(script: Path):
|
|
67
|
+
|
|
68
|
+
script_path = Path.cwd()
|
|
69
|
+
if script.suffix == ".py":
|
|
70
|
+
script_path = script_path.joinpath(Path(script))
|
|
71
|
+
else:
|
|
72
|
+
script_path = script_path.joinpath(Path(script).with_suffix(".py"))
|
|
73
|
+
|
|
74
|
+
if not script_path.exists():
|
|
75
|
+
print(f"❌ Script '{script_path}' not found")
|
|
76
|
+
sys.exit(1)
|
|
77
|
+
|
|
78
|
+
sp.run(["uv", "run", "python", "-Xgil=0", script_path])
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def display_logo():
|
|
82
|
+
print(" _____ _ _ __ __ _ ")
|
|
83
|
+
print(" / ____| (_) \\ \\ / / | | ")
|
|
84
|
+
print(" | (___ | |_ _ __ ___ __\\ \\ /\\ / /__| |__ ")
|
|
85
|
+
print(" ___ \\| | | '_ ` _ \\ / _ \\ \\/ \\/ / _ \\ '_ \\ ")
|
|
86
|
+
print(" ____) | | | | | | | | __/\\ /\\ / __/ |_) |")
|
|
87
|
+
print(" |_____/|_|_|_| |_| |_|\\___| \\/ \\/ \\___|_.__/ ")
|
|
88
|
+
print("Version: 0.1.0\t\t\t Author: S.Abilash")
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def main():
|
|
92
|
+
display_logo()
|
|
93
|
+
args = sys.argv[1:]
|
|
94
|
+
|
|
95
|
+
if not args:
|
|
96
|
+
print("Usage:")
|
|
97
|
+
print(" slime new <project_name>")
|
|
98
|
+
print(" slime run <script>")
|
|
99
|
+
sys.exit(1)
|
|
100
|
+
|
|
101
|
+
command = args[0]
|
|
102
|
+
|
|
103
|
+
if command == "new":
|
|
104
|
+
if len(args) != 2:
|
|
105
|
+
print("Usage: slime new <project_name>")
|
|
106
|
+
sys.exit(1)
|
|
107
|
+
|
|
108
|
+
create_project(args[1])
|
|
109
|
+
|
|
110
|
+
elif command == "run":
|
|
111
|
+
if len(args) != 2:
|
|
112
|
+
print("Usage: slime run <script>")
|
|
113
|
+
sys.exit(1)
|
|
114
|
+
|
|
115
|
+
parent_path = Path(args[0]).parent
|
|
116
|
+
project_path = parent_path.joinpath(Path(args[1]))
|
|
117
|
+
run_project(project_path)
|
|
118
|
+
|
|
119
|
+
else:
|
|
120
|
+
print(f"Unknown command: {command}")
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
if __name__ == "__main__":
|
|
124
|
+
main()
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
# AUTHOR: S.ABILASH
|
|
2
|
+
# Email: abinix01@gmail.com
|
|
3
|
+
|
|
4
|
+
import inspect
|
|
5
|
+
from typing import Callable, Dict
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class Routes:
|
|
9
|
+
def __init__(
|
|
10
|
+
self,
|
|
11
|
+
path: str = "/",
|
|
12
|
+
method: str = "GET",
|
|
13
|
+
stream: str | None = None,
|
|
14
|
+
ws: bool = False,
|
|
15
|
+
) -> None:
|
|
16
|
+
if method.upper() not in ["GET", "POST", "PATCH", "PUT", "DELETE", "OPTIONS"]:
|
|
17
|
+
raise ValueError(f"{method} is not Valid")
|
|
18
|
+
|
|
19
|
+
self.path: str = path
|
|
20
|
+
self.method: str = method
|
|
21
|
+
self.stream: str | None = stream
|
|
22
|
+
self.ws: bool = ws
|
|
23
|
+
|
|
24
|
+
def __hash__(self) -> int:
|
|
25
|
+
return hash((self.path, self.method, self.stream))
|
|
26
|
+
|
|
27
|
+
def __eq__(self, value: object) -> bool:
|
|
28
|
+
return (
|
|
29
|
+
isinstance(value, Routes)
|
|
30
|
+
and self.path == value.path
|
|
31
|
+
and self.method == value.method
|
|
32
|
+
and self.stream == value.stream
|
|
33
|
+
and self.ws == value.ws
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
def __str__(self) -> str:
|
|
37
|
+
return f"""
|
|
38
|
+
Path: {self.path}
|
|
39
|
+
Method: {self.method}
|
|
40
|
+
{f"Stream: {self.stream}" if self.stream is not None else ""}
|
|
41
|
+
{f"Websocket: {self.ws}" if self.ws is not None else ""}
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class Slime:
|
|
46
|
+
def __init__(self, filename: str) -> None:
|
|
47
|
+
if filename is None or not isinstance(filename, str):
|
|
48
|
+
raise ValueError("Need argument as __file__ ")
|
|
49
|
+
|
|
50
|
+
# for templating purpose we can fetch file path with /templates and /static
|
|
51
|
+
self.__filename: str | None = filename
|
|
52
|
+
|
|
53
|
+
# => you can multiple or same path with different request
|
|
54
|
+
# like let saay user can assign a path /name
|
|
55
|
+
# with methods like GET,POST under /name
|
|
56
|
+
# => for each handler there can be only one method
|
|
57
|
+
self.__routes: Dict[Routes, Dict[str, Callable | None]] = {}
|
|
58
|
+
|
|
59
|
+
def middle_before(self, path: str = "/", method: str = "GET") -> Callable:
|
|
60
|
+
def wrapper(middle_handler) -> Callable:
|
|
61
|
+
if middle_handler is None or not callable(middle_handler):
|
|
62
|
+
raise ValueError(
|
|
63
|
+
f"Middleware handler should be a function for [Path: {path}, Method: {method}]"
|
|
64
|
+
)
|
|
65
|
+
if inspect.iscoroutinefunction(middle_handler):
|
|
66
|
+
raise Exception("Slime currently doesnt support async handler.")
|
|
67
|
+
found: bool = False
|
|
68
|
+
if path == "*":
|
|
69
|
+
for route in self.__routes:
|
|
70
|
+
call_handler = self.__routes.get(route)
|
|
71
|
+
if call_handler is not None:
|
|
72
|
+
call_handler["before"] = middle_handler
|
|
73
|
+
else:
|
|
74
|
+
for route in self.__routes:
|
|
75
|
+
if route.path == path and route.method == method:
|
|
76
|
+
call_handler = self.__routes.get(route)
|
|
77
|
+
if call_handler is not None:
|
|
78
|
+
call_handler["before"] = middle_handler
|
|
79
|
+
found = True
|
|
80
|
+
break
|
|
81
|
+
if not found:
|
|
82
|
+
raise ValueError(
|
|
83
|
+
"You need to define the request handler to declare middleware"
|
|
84
|
+
)
|
|
85
|
+
return middle_handler
|
|
86
|
+
|
|
87
|
+
return wrapper
|
|
88
|
+
|
|
89
|
+
def middle_after(self, path: str = "/", method: str = "GET") -> Callable:
|
|
90
|
+
def wrapper(middle_handler) -> Callable:
|
|
91
|
+
if middle_handler is None or not callable(middle_handler):
|
|
92
|
+
raise ValueError(
|
|
93
|
+
f"Middleware handler should be a function for [Path: {path}, Method: {method}]"
|
|
94
|
+
)
|
|
95
|
+
if inspect.iscoroutinefunction(middle_handler):
|
|
96
|
+
raise Exception("Slime currently doesnt support async handler.")
|
|
97
|
+
found: bool = False
|
|
98
|
+
if path == "*":
|
|
99
|
+
for route in self.__routes:
|
|
100
|
+
call_handler = self.__routes.get(route)
|
|
101
|
+
if call_handler is not None:
|
|
102
|
+
call_handler["before"] = middle_handler
|
|
103
|
+
else:
|
|
104
|
+
for route in self.__routes:
|
|
105
|
+
if route.path == path and route.method == method:
|
|
106
|
+
call_handler = self.__routes.get(route)
|
|
107
|
+
if call_handler is not None:
|
|
108
|
+
call_handler["after"] = middle_handler
|
|
109
|
+
found = True
|
|
110
|
+
break
|
|
111
|
+
if not found:
|
|
112
|
+
raise ValueError(
|
|
113
|
+
"You need to define the request handler to declare middleware"
|
|
114
|
+
)
|
|
115
|
+
return middle_handler
|
|
116
|
+
|
|
117
|
+
return wrapper
|
|
118
|
+
|
|
119
|
+
def route(
|
|
120
|
+
self,
|
|
121
|
+
path: str = "/",
|
|
122
|
+
method: str = "GET",
|
|
123
|
+
stream: str | None = None,
|
|
124
|
+
ws: bool = False,
|
|
125
|
+
) -> Callable:
|
|
126
|
+
def wrapper(route_handler) -> Callable:
|
|
127
|
+
if route_handler is None or not callable(route_handler):
|
|
128
|
+
raise ValueError(
|
|
129
|
+
f"Route handler should be a function for [Path: {path}, Method: {method}]"
|
|
130
|
+
)
|
|
131
|
+
if inspect.iscoroutinefunction(route_handler):
|
|
132
|
+
raise Exception("Slime currently doesnt support async handler.")
|
|
133
|
+
self.__routes[Routes(path, method, stream, ws)] = {
|
|
134
|
+
"handler": route_handler,
|
|
135
|
+
"before": None,
|
|
136
|
+
"after": None,
|
|
137
|
+
}
|
|
138
|
+
return route_handler
|
|
139
|
+
|
|
140
|
+
return wrapper
|
|
141
|
+
|
|
142
|
+
def stream(
|
|
143
|
+
self, path: str = "/", method: str = "GET", content: str = "text/plain"
|
|
144
|
+
) -> Callable:
|
|
145
|
+
def wrapper(stream_handler) -> Callable:
|
|
146
|
+
if stream_handler is None or not callable(stream_handler):
|
|
147
|
+
raise ValueError(
|
|
148
|
+
f"Stream handler should be a function for [Path: {path}, Method: {method}]"
|
|
149
|
+
)
|
|
150
|
+
if not isinstance(content, str):
|
|
151
|
+
raise ValueError(
|
|
152
|
+
f"Stream content type should be of type <String> with MIME for [Path: {path}, Method: {method}]"
|
|
153
|
+
)
|
|
154
|
+
self.__routes[Routes(path, method, stream=content)] = {
|
|
155
|
+
"handler": stream_handler,
|
|
156
|
+
"before": None,
|
|
157
|
+
"after": None,
|
|
158
|
+
}
|
|
159
|
+
return stream_handler
|
|
160
|
+
|
|
161
|
+
return wrapper
|
|
162
|
+
|
|
163
|
+
def websocket(self, path: str = "/", method: str = "GET") -> Callable:
|
|
164
|
+
def wrapper(websocket_handler) -> Callable:
|
|
165
|
+
if websocket_handler is None or not callable(websocket_handler):
|
|
166
|
+
raise ValueError(
|
|
167
|
+
f"Websocket handler should be a function for [Path: {path}, Method: {method}]"
|
|
168
|
+
)
|
|
169
|
+
self.__routes[Routes(path, method, stream=None, ws=True)] = {
|
|
170
|
+
"handler": websocket_handler,
|
|
171
|
+
"before": None,
|
|
172
|
+
"after": None,
|
|
173
|
+
}
|
|
174
|
+
return websocket_handler
|
|
175
|
+
|
|
176
|
+
return wrapper
|
|
177
|
+
|
|
178
|
+
def _get_routes(self) -> Dict[Routes, Dict[str, Callable | None]]:
|
|
179
|
+
return self.__routes
|
|
180
|
+
|
|
181
|
+
def serve(
|
|
182
|
+
self,
|
|
183
|
+
host: str = "127.0.0.1",
|
|
184
|
+
port: int = 3000,
|
|
185
|
+
secret_key: str | None = None,
|
|
186
|
+
dev: bool = False,
|
|
187
|
+
) -> None:
|
|
188
|
+
if secret_key is None:
|
|
189
|
+
import secrets
|
|
190
|
+
|
|
191
|
+
secret_key = secrets.token_urlsafe(30)
|
|
192
|
+
|
|
193
|
+
import web
|
|
194
|
+
|
|
195
|
+
web.init_web(self, host, port, secret_key, dev)
|
|
196
|
+
print("Slime server is shutting down...")
|
|
197
|
+
print("Finished")
|
|
Binary file
|
|
Binary file
|