webenginepy 0.2.1__tar.gz → 1.2.2__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.
- {webenginepy-0.2.1/webenginepy.egg-info → webenginepy-1.2.2}/PKG-INFO +4 -1
- {webenginepy-0.2.1 → webenginepy-1.2.2}/README.md +3 -0
- {webenginepy-0.2.1 → webenginepy-1.2.2}/pyproject.toml +1 -1
- webenginepy-1.2.2/webenginepy/engine.py +205 -0
- {webenginepy-0.2.1 → webenginepy-1.2.2/webenginepy.egg-info}/PKG-INFO +4 -1
- webenginepy-0.2.1/webenginepy/engine.py +0 -207
- {webenginepy-0.2.1 → webenginepy-1.2.2}/License +0 -0
- {webenginepy-0.2.1 → webenginepy-1.2.2}/setup.cfg +0 -0
- {webenginepy-0.2.1 → webenginepy-1.2.2}/webenginepy/__init__.py +0 -0
- {webenginepy-0.2.1 → webenginepy-1.2.2}/webenginepy.egg-info/SOURCES.txt +0 -0
- {webenginepy-0.2.1 → webenginepy-1.2.2}/webenginepy.egg-info/dependency_links.txt +0 -0
- {webenginepy-0.2.1 → webenginepy-1.2.2}/webenginepy.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: webenginepy
|
|
3
|
-
Version:
|
|
3
|
+
Version: 1.2.2
|
|
4
4
|
Summary: Mini moteur web Python pour créer des sites web ultra simples sans framework
|
|
5
5
|
Author-email: Gabri <gabri@example.com>
|
|
6
6
|
License: MIT
|
|
@@ -90,3 +90,6 @@ body {
|
|
|
90
90
|
def apply(html: str) -> str:
|
|
91
91
|
return html.replace("</body>", "<!-- outil appliqué --></body>")
|
|
92
92
|
|
|
93
|
+
# POUR PLUS D'INFOS
|
|
94
|
+
Aller sur
|
|
95
|
+
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
# webenginepy/engine.py
|
|
2
|
+
|
|
3
|
+
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
|
|
4
|
+
import os
|
|
5
|
+
from typing import Dict, List, Optional
|
|
6
|
+
|
|
7
|
+
# ======================
|
|
8
|
+
# État global
|
|
9
|
+
# ======================
|
|
10
|
+
|
|
11
|
+
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
|
|
12
|
+
|
|
13
|
+
PAGES: Dict[str, dict] = {}
|
|
14
|
+
TOOLS: List[str] = []
|
|
15
|
+
|
|
16
|
+
_CURRENT_PAGE: Optional[str] = None
|
|
17
|
+
DEBUG = True
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
# ======================
|
|
21
|
+
# Page context
|
|
22
|
+
# ======================
|
|
23
|
+
|
|
24
|
+
class app:
|
|
25
|
+
"""Déclare une page web via un contexte with"""
|
|
26
|
+
|
|
27
|
+
def __init__(self, url: str, html_file: str):
|
|
28
|
+
self.url = url if url.startswith("/") else "/" + url
|
|
29
|
+
self.html_file = html_file
|
|
30
|
+
|
|
31
|
+
def __enter__(self):
|
|
32
|
+
global _CURRENT_PAGE
|
|
33
|
+
PAGES[self.url] = {
|
|
34
|
+
"file": self.html_file,
|
|
35
|
+
"static": [],
|
|
36
|
+
"buttons": {},
|
|
37
|
+
"headers": {}
|
|
38
|
+
}
|
|
39
|
+
_CURRENT_PAGE = self.url
|
|
40
|
+
return self
|
|
41
|
+
|
|
42
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
43
|
+
global _CURRENT_PAGE
|
|
44
|
+
_CURRENT_PAGE = None
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
# ======================
|
|
48
|
+
# Déclaration logique
|
|
49
|
+
# ======================
|
|
50
|
+
|
|
51
|
+
def route(value: str, target: Optional[str] = None):
|
|
52
|
+
if _CURRENT_PAGE is None:
|
|
53
|
+
raise RuntimeError("route() doit être utilisé dans un bloc with app()")
|
|
54
|
+
|
|
55
|
+
page = PAGES[_CURRENT_PAGE]
|
|
56
|
+
|
|
57
|
+
if value.startswith("server/"):
|
|
58
|
+
page["static"].append(value)
|
|
59
|
+
return
|
|
60
|
+
|
|
61
|
+
if value.startswith("button:") and target:
|
|
62
|
+
name = value.split(":", 1)[1]
|
|
63
|
+
page["buttons"][name] = target
|
|
64
|
+
return
|
|
65
|
+
|
|
66
|
+
if value.startswith("header:") and target:
|
|
67
|
+
name = value.split(":", 1)[1]
|
|
68
|
+
page["headers"][name] = target
|
|
69
|
+
return
|
|
70
|
+
|
|
71
|
+
raise ValueError(f"Route invalide : {value}")
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
# ======================
|
|
75
|
+
# Tools system
|
|
76
|
+
# ======================
|
|
77
|
+
|
|
78
|
+
def tool(name: str):
|
|
79
|
+
if name not in TOOLS:
|
|
80
|
+
TOOLS.append(name)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def apply_tools(html: str) -> str:
|
|
84
|
+
for name in TOOLS:
|
|
85
|
+
path = os.path.join(BASE_DIR, "tools", f"{name}.py")
|
|
86
|
+
if not os.path.exists(path):
|
|
87
|
+
log(f"[WARN] tool '{name}' introuvable")
|
|
88
|
+
continue
|
|
89
|
+
|
|
90
|
+
scope = {}
|
|
91
|
+
try:
|
|
92
|
+
exec(open(path, encoding="utf-8").read(), scope)
|
|
93
|
+
if "apply" in scope:
|
|
94
|
+
html = scope["apply"](html)
|
|
95
|
+
except Exception as e:
|
|
96
|
+
log(f"[ERROR] tool '{name}': {e}")
|
|
97
|
+
|
|
98
|
+
return html
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
# ======================
|
|
102
|
+
# HTML injection helpers
|
|
103
|
+
# ======================
|
|
104
|
+
|
|
105
|
+
def inject_buttons(html: str, buttons: dict) -> str:
|
|
106
|
+
for name, link in buttons.items():
|
|
107
|
+
html = html.replace(
|
|
108
|
+
"</body>",
|
|
109
|
+
f'<a href="{link}"><button>{name}</button></a>\n</body>'
|
|
110
|
+
)
|
|
111
|
+
return html
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def inject_static(html: str, static_files: list) -> str:
|
|
115
|
+
for s in static_files:
|
|
116
|
+
if s.endswith(".css"):
|
|
117
|
+
html = html.replace(
|
|
118
|
+
"</head>",
|
|
119
|
+
f'<link rel="stylesheet" href="/{s}">\n</head>'
|
|
120
|
+
)
|
|
121
|
+
elif s.endswith(".js"):
|
|
122
|
+
html = html.replace(
|
|
123
|
+
"</body>",
|
|
124
|
+
f'<script src="/{s}"></script>\n</body>'
|
|
125
|
+
)
|
|
126
|
+
return html
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
# ======================
|
|
130
|
+
# HTTP helpers
|
|
131
|
+
# ======================
|
|
132
|
+
|
|
133
|
+
def send_file(handler, path: str):
|
|
134
|
+
if not os.path.exists(path):
|
|
135
|
+
handler.send_error(404)
|
|
136
|
+
return
|
|
137
|
+
|
|
138
|
+
handler.send_response(200)
|
|
139
|
+
|
|
140
|
+
if path.endswith(".css"):
|
|
141
|
+
handler.send_header("Content-Type", "text/css")
|
|
142
|
+
elif path.endswith(".js"):
|
|
143
|
+
handler.send_header("Content-Type", "application/javascript")
|
|
144
|
+
else:
|
|
145
|
+
handler.send_header("Content-Type", "application/octet-stream")
|
|
146
|
+
|
|
147
|
+
handler.end_headers()
|
|
148
|
+
handler.wfile.write(open(path, "rb").read())
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def log(msg: str):
|
|
152
|
+
if DEBUG:
|
|
153
|
+
print(msg)
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
# ======================
|
|
157
|
+
# Server
|
|
158
|
+
# ======================
|
|
159
|
+
|
|
160
|
+
def run(host: str = "localhost", port: int = 8000):
|
|
161
|
+
|
|
162
|
+
class Handler(BaseHTTPRequestHandler):
|
|
163
|
+
|
|
164
|
+
def do_GET(self):
|
|
165
|
+
path = self.path
|
|
166
|
+
|
|
167
|
+
if path in PAGES:
|
|
168
|
+
self.serve_page(path)
|
|
169
|
+
elif path.startswith("/server/"):
|
|
170
|
+
send_file(self, os.path.join(BASE_DIR, path[1:]))
|
|
171
|
+
else:
|
|
172
|
+
self.send_error(404, "Route inconnue")
|
|
173
|
+
|
|
174
|
+
log(f"[GET] {path}")
|
|
175
|
+
|
|
176
|
+
def serve_page(self, path: str):
|
|
177
|
+
page = PAGES[path]
|
|
178
|
+
file_path = os.path.join(BASE_DIR, page["file"])
|
|
179
|
+
|
|
180
|
+
if not os.path.exists(file_path):
|
|
181
|
+
self.send_error(404, "HTML introuvable")
|
|
182
|
+
return
|
|
183
|
+
|
|
184
|
+
html = open(file_path, encoding="utf-8").read()
|
|
185
|
+
html = inject_buttons(html, page["buttons"])
|
|
186
|
+
html = inject_static(html, page["static"])
|
|
187
|
+
html = apply_tools(html)
|
|
188
|
+
|
|
189
|
+
self.send_response(200)
|
|
190
|
+
for h, v in page["headers"].items():
|
|
191
|
+
self.send_header(h, v)
|
|
192
|
+
self.send_header("Content-Type", "text/html; charset=utf-8")
|
|
193
|
+
self.end_headers()
|
|
194
|
+
self.wfile.write(html.encode("utf-8"))
|
|
195
|
+
|
|
196
|
+
def log_message(self, *_):
|
|
197
|
+
if DEBUG:
|
|
198
|
+
super().log_message(*_)
|
|
199
|
+
|
|
200
|
+
print("WebEnginePy lancé")
|
|
201
|
+
print(f"→ http://{host}:{port}")
|
|
202
|
+
print("→ Pages :", list(PAGES.keys()))
|
|
203
|
+
print("→ Tools :", TOOLS)
|
|
204
|
+
|
|
205
|
+
ThreadingHTTPServer((host, port), Handler).serve_forever()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: webenginepy
|
|
3
|
-
Version:
|
|
3
|
+
Version: 1.2.2
|
|
4
4
|
Summary: Mini moteur web Python pour créer des sites web ultra simples sans framework
|
|
5
5
|
Author-email: Gabri <gabri@example.com>
|
|
6
6
|
License: MIT
|
|
@@ -90,3 +90,6 @@ body {
|
|
|
90
90
|
def apply(html: str) -> str:
|
|
91
91
|
return html.replace("</body>", "<!-- outil appliqué --></body>")
|
|
92
92
|
|
|
93
|
+
# POUR PLUS D'INFOS
|
|
94
|
+
Aller sur
|
|
95
|
+
|
|
@@ -1,207 +0,0 @@
|
|
|
1
|
-
# webenginepy/engine.py
|
|
2
|
-
|
|
3
|
-
from http.server import BaseHTTPRequestHandler, HTTPServer
|
|
4
|
-
import os
|
|
5
|
-
from typing import Dict, List, Optional
|
|
6
|
-
|
|
7
|
-
# ======================
|
|
8
|
-
# Données globales
|
|
9
|
-
# ======================
|
|
10
|
-
|
|
11
|
-
BASE_DIR = os.getcwd()
|
|
12
|
-
|
|
13
|
-
PAGES: Dict[str, dict] = {}
|
|
14
|
-
TOOLS: List[str] = []
|
|
15
|
-
|
|
16
|
-
_current_page: Optional[str] = None
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
# ======================
|
|
20
|
-
# Déclaration des pages
|
|
21
|
-
# ======================
|
|
22
|
-
|
|
23
|
-
class app:
|
|
24
|
-
"""
|
|
25
|
-
Déclare une page web.
|
|
26
|
-
|
|
27
|
-
Utilisation :
|
|
28
|
-
with app("/", "index.html"):
|
|
29
|
-
route("server/style.css")
|
|
30
|
-
route("button:page2", "/page2")
|
|
31
|
-
"""
|
|
32
|
-
|
|
33
|
-
def __init__(self, url: str, file: str):
|
|
34
|
-
self.url = url
|
|
35
|
-
self.file = file
|
|
36
|
-
|
|
37
|
-
def __enter__(self):
|
|
38
|
-
global _current_page
|
|
39
|
-
|
|
40
|
-
if not self.url.startswith("/"):
|
|
41
|
-
self.url = "/" + self.url
|
|
42
|
-
|
|
43
|
-
PAGES[self.url] = {
|
|
44
|
-
"file": self.file,
|
|
45
|
-
"static": [],
|
|
46
|
-
"buttons": {}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
_current_page = self.url
|
|
50
|
-
return self
|
|
51
|
-
|
|
52
|
-
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
53
|
-
global _current_page
|
|
54
|
-
_current_page = None
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
# ======================
|
|
58
|
-
# Routes internes
|
|
59
|
-
# ======================
|
|
60
|
-
|
|
61
|
-
def route(value: str, target: Optional[str] = None):
|
|
62
|
-
"""
|
|
63
|
-
Ajoute une route liée à la page courante
|
|
64
|
-
|
|
65
|
-
- route("server/style.css")
|
|
66
|
-
- route("button:page2", "/page2")
|
|
67
|
-
"""
|
|
68
|
-
if _current_page is None:
|
|
69
|
-
raise RuntimeError("route() doit être utilisé dans un bloc with app()")
|
|
70
|
-
|
|
71
|
-
page = PAGES[_current_page]
|
|
72
|
-
|
|
73
|
-
# Fichier statique
|
|
74
|
-
if value.startswith("server/"):
|
|
75
|
-
page["static"].append(value)
|
|
76
|
-
|
|
77
|
-
# Bouton / lien
|
|
78
|
-
elif value.startswith("button:") and target:
|
|
79
|
-
name = value.split(":", 1)[1]
|
|
80
|
-
page["buttons"][name] = target
|
|
81
|
-
|
|
82
|
-
else:
|
|
83
|
-
raise ValueError(f"Route invalide : {value}")
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
# ======================
|
|
87
|
-
# Outils (plugins)
|
|
88
|
-
# ======================
|
|
89
|
-
|
|
90
|
-
def tool(name: str):
|
|
91
|
-
"""
|
|
92
|
-
Active un outil (tools/nom.py)
|
|
93
|
-
"""
|
|
94
|
-
if name not in TOOLS:
|
|
95
|
-
TOOLS.append(name)
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
def _apply_tools(html: str) -> str:
|
|
99
|
-
"""
|
|
100
|
-
Applique tous les outils activés au HTML
|
|
101
|
-
"""
|
|
102
|
-
for t in TOOLS:
|
|
103
|
-
tool_path = os.path.join(BASE_DIR, "tools", f"{t}.py")
|
|
104
|
-
|
|
105
|
-
if not os.path.exists(tool_path):
|
|
106
|
-
print(f"[WARN] outil '{t}' introuvable")
|
|
107
|
-
continue
|
|
108
|
-
|
|
109
|
-
scope = {}
|
|
110
|
-
try:
|
|
111
|
-
with open(tool_path, encoding="utf-8") as f:
|
|
112
|
-
exec(f.read(), scope)
|
|
113
|
-
|
|
114
|
-
if "apply" in scope:
|
|
115
|
-
html = scope["apply"](html)
|
|
116
|
-
|
|
117
|
-
except Exception as e:
|
|
118
|
-
print(f"[ERROR] outil '{t}': {e}")
|
|
119
|
-
|
|
120
|
-
return html
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
# ======================
|
|
124
|
-
# Serveur HTTP
|
|
125
|
-
# ======================
|
|
126
|
-
|
|
127
|
-
def run(host: str = "localhost", port: int = 8000):
|
|
128
|
-
"""
|
|
129
|
-
Lance le serveur WebEnginePy
|
|
130
|
-
"""
|
|
131
|
-
|
|
132
|
-
class Handler(BaseHTTPRequestHandler):
|
|
133
|
-
|
|
134
|
-
def do_GET(self):
|
|
135
|
-
# ======================
|
|
136
|
-
# Pages HTML
|
|
137
|
-
# ======================
|
|
138
|
-
if self.path in PAGES:
|
|
139
|
-
page = PAGES[self.path]
|
|
140
|
-
file_path = os.path.join(BASE_DIR, page["file"])
|
|
141
|
-
|
|
142
|
-
if not os.path.exists(file_path):
|
|
143
|
-
self.send_error(404, "Fichier HTML introuvable")
|
|
144
|
-
return
|
|
145
|
-
|
|
146
|
-
try:
|
|
147
|
-
with open(file_path, encoding="utf-8") as f:
|
|
148
|
-
html = f.read()
|
|
149
|
-
|
|
150
|
-
# Injection boutons (simple)
|
|
151
|
-
for name, link in page["buttons"].items():
|
|
152
|
-
button_html = f'<a href="{link}"><button>{name}</button></a>'
|
|
153
|
-
html = html.replace("</body>", button_html + "\n</body>")
|
|
154
|
-
|
|
155
|
-
# Outils
|
|
156
|
-
html = _apply_tools(html)
|
|
157
|
-
|
|
158
|
-
self.send_response(200)
|
|
159
|
-
self.send_header("Content-Type", "text/html; charset=utf-8")
|
|
160
|
-
self.end_headers()
|
|
161
|
-
self.wfile.write(html.encode("utf-8"))
|
|
162
|
-
|
|
163
|
-
except Exception as e:
|
|
164
|
-
self.send_error(500, str(e))
|
|
165
|
-
|
|
166
|
-
# ======================
|
|
167
|
-
# Fichiers statiques
|
|
168
|
-
# ======================
|
|
169
|
-
elif self.path.startswith("/server/"):
|
|
170
|
-
file_path = os.path.join(BASE_DIR, self.path[1:])
|
|
171
|
-
|
|
172
|
-
if not os.path.exists(file_path):
|
|
173
|
-
self.send_error(404)
|
|
174
|
-
return
|
|
175
|
-
|
|
176
|
-
self.send_response(200)
|
|
177
|
-
|
|
178
|
-
if file_path.endswith(".css"):
|
|
179
|
-
self.send_header("Content-Type", "text/css")
|
|
180
|
-
elif file_path.endswith(".js"):
|
|
181
|
-
self.send_header("Content-Type", "application/javascript")
|
|
182
|
-
else:
|
|
183
|
-
self.send_header("Content-Type", "application/octet-stream")
|
|
184
|
-
|
|
185
|
-
self.end_headers()
|
|
186
|
-
|
|
187
|
-
with open(file_path, "rb") as f:
|
|
188
|
-
self.wfile.write(f.read())
|
|
189
|
-
|
|
190
|
-
# ======================
|
|
191
|
-
# Inconnu
|
|
192
|
-
# ======================
|
|
193
|
-
else:
|
|
194
|
-
self.send_error(404, "Route inconnue")
|
|
195
|
-
|
|
196
|
-
def log_message(self, *args):
|
|
197
|
-
print(f"[REQ] {self.command} {self.path}")
|
|
198
|
-
|
|
199
|
-
# ======================
|
|
200
|
-
# Infos serveur
|
|
201
|
-
# ======================
|
|
202
|
-
print("WebEnginePy démarré")
|
|
203
|
-
print(f"→ http://{host}:{port}")
|
|
204
|
-
print("→ Pages :", list(PAGES.keys()))
|
|
205
|
-
print("→ Outils :", TOOLS)
|
|
206
|
-
|
|
207
|
-
HTTPServer((host, port), Handler).serve_forever()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|