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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: webenginepy
3
- Version: 0.2.1
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
+
@@ -73,3 +73,6 @@ body {
73
73
  def apply(html: str) -> str:
74
74
  return html.replace("</body>", "<!-- outil appliqué --></body>")
75
75
 
76
+ # POUR PLUS D'INFOS
77
+ Aller sur
78
+
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "webenginepy"
7
- version = "0.2.1"
7
+ version = "1.2.2"
8
8
  description = "Mini moteur web Python pour créer des sites web ultra simples sans framework"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.8"
@@ -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: 0.2.1
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