fastled 1.0.12__py2.py3-none-any.whl → 1.0.17__py2.py3-none-any.whl
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.
- {fastled-1.0.12.dist-info → fastled-1.0.17.dist-info}/METADATA +2 -15
- fastled-1.0.17.dist-info/RECORD +18 -0
- fastled-1.0.17.dist-info/entry_points.txt +3 -0
- fastled-1.0.17.dist-info/top_level.txt +2 -0
- fled/__init__.py +0 -0
- {fastled → fled}/app.py +86 -161
- fled/check_cpp_syntax.py +34 -0
- {fastled → fled}/cli.py +1 -1
- {fastled → fled}/compile_server.py +207 -222
- {fastled → fled}/docker_manager.py +7 -11
- {fastled → fled}/filewatcher.py +146 -196
- {fastled → fled}/open_browser.py +1 -5
- fled/web_compile.py +173 -0
- fastled/__init__.py +0 -3
- fastled/keyboard.py +0 -89
- fastled/sketch.py +0 -55
- fastled/util.py +0 -10
- fastled/web_compile.py +0 -284
- fastled-1.0.12.dist-info/RECORD +0 -20
- fastled-1.0.12.dist-info/entry_points.txt +0 -4
- fastled-1.0.12.dist-info/top_level.txt +0 -2
- {fastled-1.0.12.dist-info → fastled-1.0.17.dist-info}/LICENSE +0 -0
- {fastled-1.0.12.dist-info → fastled-1.0.17.dist-info}/WHEEL +0 -0
- {fastled → fled}/assets/example.txt +0 -0
- {fastled → fled}/build_mode.py +0 -0
- {fastled → fled}/paths.py +0 -0
@@ -1,222 +1,207 @@
|
|
1
|
-
import socket
|
2
|
-
import subprocess
|
3
|
-
import
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
from
|
10
|
-
|
11
|
-
_DEFAULT_CONTAINER_NAME = "fastled-wasm-compiler"
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
def
|
17
|
-
"""Find an available port starting from the given port."""
|
18
|
-
port = start_port
|
19
|
-
end_port = start_port + 1000
|
20
|
-
while True:
|
21
|
-
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
|
22
|
-
if s.connect_ex(("localhost", port)) != 0:
|
23
|
-
return port
|
24
|
-
port += 1
|
25
|
-
if port >= end_port:
|
26
|
-
raise RuntimeError("No available ports found")
|
27
|
-
|
28
|
-
|
29
|
-
class CompileServer:
|
30
|
-
def __init__(
|
31
|
-
self,
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
self.
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
print("
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
print("
|
128
|
-
port
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
except subprocess.TimeoutExpired:
|
209
|
-
# Force kill if it doesn't stop gracefully
|
210
|
-
if self.running_process:
|
211
|
-
self.running_process.kill()
|
212
|
-
self.running_process.wait()
|
213
|
-
except KeyboardInterrupt:
|
214
|
-
if self.running_process:
|
215
|
-
self.running_process.kill()
|
216
|
-
self.running_process.wait()
|
217
|
-
except Exception as e:
|
218
|
-
print(f"Error stopping Docker container: {e}")
|
219
|
-
finally:
|
220
|
-
self.running_process = None
|
221
|
-
self.running = False
|
222
|
-
print("Compile server stopped")
|
1
|
+
import socket
|
2
|
+
import subprocess
|
3
|
+
import threading
|
4
|
+
import time
|
5
|
+
from typing import Optional
|
6
|
+
|
7
|
+
import httpx
|
8
|
+
|
9
|
+
from fled.docker_manager import DockerManager
|
10
|
+
|
11
|
+
_DEFAULT_CONTAINER_NAME = "fastled-wasm-compiler"
|
12
|
+
|
13
|
+
_DEFAULT_START_PORT = 9021
|
14
|
+
|
15
|
+
|
16
|
+
def _find_available_port(start_port: int = _DEFAULT_START_PORT) -> int:
|
17
|
+
"""Find an available port starting from the given port."""
|
18
|
+
port = start_port
|
19
|
+
end_port = start_port + 1000
|
20
|
+
while True:
|
21
|
+
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
|
22
|
+
if s.connect_ex(("localhost", port)) != 0:
|
23
|
+
return port
|
24
|
+
port += 1
|
25
|
+
if port >= end_port:
|
26
|
+
raise RuntimeError("No available ports found")
|
27
|
+
|
28
|
+
|
29
|
+
class CompileServer:
|
30
|
+
def __init__(
|
31
|
+
self, container_name=_DEFAULT_CONTAINER_NAME, disable_auto_clean: bool = False
|
32
|
+
) -> None:
|
33
|
+
self.container_name = container_name
|
34
|
+
self.disable_auto_clean = disable_auto_clean
|
35
|
+
self.docker = DockerManager(container_name=container_name)
|
36
|
+
self.running = False
|
37
|
+
self.thread: Optional[threading.Thread] = None
|
38
|
+
self.running_process: subprocess.Popen | None = None
|
39
|
+
self._port = self.start()
|
40
|
+
|
41
|
+
def port(self) -> int:
|
42
|
+
return self._port
|
43
|
+
|
44
|
+
def url(self) -> str:
|
45
|
+
return f"http://localhost:{self._port}"
|
46
|
+
|
47
|
+
def wait_for_startup(self, timeout: int = 100) -> bool:
|
48
|
+
"""Wait for the server to start up."""
|
49
|
+
start_time = time.time()
|
50
|
+
while time.time() - start_time < timeout:
|
51
|
+
# ping the server to see if it's up
|
52
|
+
if not self._port:
|
53
|
+
return False
|
54
|
+
# use httpx to ping the server
|
55
|
+
# if successful, return True
|
56
|
+
try:
|
57
|
+
response = httpx.get(f"http://localhost:{self._port}")
|
58
|
+
if response.status_code < 400:
|
59
|
+
return True
|
60
|
+
except KeyboardInterrupt:
|
61
|
+
raise
|
62
|
+
except Exception:
|
63
|
+
pass
|
64
|
+
time.sleep(0.1)
|
65
|
+
if not self.running:
|
66
|
+
return False
|
67
|
+
return False
|
68
|
+
|
69
|
+
def start(self) -> int:
|
70
|
+
print("Compiling server starting")
|
71
|
+
self.running = True
|
72
|
+
# Ensure Docker is running
|
73
|
+
with self.docker.get_lock():
|
74
|
+
if not self.docker.is_running():
|
75
|
+
if not self.docker.start():
|
76
|
+
print("Docker could not be started. Exiting.")
|
77
|
+
raise RuntimeError("Docker could not be started. Exiting.")
|
78
|
+
|
79
|
+
# Clean up any existing container with the same name
|
80
|
+
|
81
|
+
try:
|
82
|
+
container_exists = (
|
83
|
+
subprocess.run(
|
84
|
+
["docker", "inspect", self.container_name],
|
85
|
+
capture_output=True,
|
86
|
+
text=True,
|
87
|
+
).returncode
|
88
|
+
== 0
|
89
|
+
)
|
90
|
+
if container_exists:
|
91
|
+
print("Cleaning up existing container")
|
92
|
+
subprocess.run(
|
93
|
+
["docker", "rm", "-f", self.container_name],
|
94
|
+
check=False,
|
95
|
+
)
|
96
|
+
except KeyboardInterrupt:
|
97
|
+
raise
|
98
|
+
except Exception as e:
|
99
|
+
print(f"Warning: Failed to remove existing container: {e}")
|
100
|
+
|
101
|
+
print("Ensuring Docker image exists")
|
102
|
+
if not self.docker.ensure_image_exists():
|
103
|
+
print("Failed to ensure Docker image exists.")
|
104
|
+
raise RuntimeError("Failed to ensure Docker image exists")
|
105
|
+
|
106
|
+
print("Docker image now validated")
|
107
|
+
|
108
|
+
# Remove the image to force a fresh download
|
109
|
+
# subprocess.run(["docker", "rmi", "fastled-wasm"], capture_output=True)
|
110
|
+
# print("All clean")
|
111
|
+
|
112
|
+
port = _find_available_port()
|
113
|
+
# server_command = ["python", "/js/run.py", "server", "--allow-shutdown"]
|
114
|
+
server_command = ["python", "/js/run.py", "server"]
|
115
|
+
if self.disable_auto_clean:
|
116
|
+
server_command.append("--disable-auto-clean")
|
117
|
+
print(f"Started Docker container with command: {server_command}")
|
118
|
+
ports = {port: 80}
|
119
|
+
self.running_process = self.docker.run_container(server_command, ports=ports)
|
120
|
+
time.sleep(3)
|
121
|
+
if self.running_process.poll() is not None:
|
122
|
+
print("Server failed to start")
|
123
|
+
self.running = False
|
124
|
+
raise RuntimeError("Server failed to start")
|
125
|
+
self.thread = threading.Thread(target=self._server_loop, daemon=True)
|
126
|
+
self.thread.start()
|
127
|
+
print("Compile server started")
|
128
|
+
return port
|
129
|
+
|
130
|
+
def stop(self) -> None:
|
131
|
+
print(f"Stopping server on port {self._port}")
|
132
|
+
# attempt to send a shutdown signal to the server
|
133
|
+
# httpx.get(f"http://localhost:{self._port}/shutdown", timeout=2)
|
134
|
+
if self.running_process:
|
135
|
+
try:
|
136
|
+
# Stop the Docker container
|
137
|
+
cp: subprocess.CompletedProcess
|
138
|
+
cp = subprocess.run(
|
139
|
+
["docker", "stop", self.container_name],
|
140
|
+
capture_output=True,
|
141
|
+
text=True,
|
142
|
+
check=True,
|
143
|
+
)
|
144
|
+
if cp.returncode != 0:
|
145
|
+
print(f"Failed to stop Docker container: {cp.stderr}")
|
146
|
+
|
147
|
+
cp = subprocess.run(
|
148
|
+
["docker", "rm", self.container_name],
|
149
|
+
capture_output=True,
|
150
|
+
text=True,
|
151
|
+
check=True,
|
152
|
+
)
|
153
|
+
if cp.returncode != 0:
|
154
|
+
print(f"Failed to remove Docker container: {cp.stderr}")
|
155
|
+
|
156
|
+
# Close the stdout pipe
|
157
|
+
if self.running_process.stdout:
|
158
|
+
self.running_process.stdout.close()
|
159
|
+
|
160
|
+
# Wait for the process to fully terminate with a timeout
|
161
|
+
self.running_process.wait(timeout=10)
|
162
|
+
if self.running_process.returncode is None:
|
163
|
+
# kill
|
164
|
+
self.running_process.kill()
|
165
|
+
if self.running_process.returncode is not None:
|
166
|
+
print(
|
167
|
+
f"Server stopped with return code {self.running_process.returncode}"
|
168
|
+
)
|
169
|
+
|
170
|
+
except subprocess.TimeoutExpired:
|
171
|
+
# Force kill if it doesn't stop gracefully
|
172
|
+
self.running_process.kill()
|
173
|
+
self.running_process.wait()
|
174
|
+
except KeyboardInterrupt:
|
175
|
+
self.running_process.kill()
|
176
|
+
self.running_process.wait()
|
177
|
+
except Exception as e:
|
178
|
+
print(f"Error stopping Docker container: {e}")
|
179
|
+
finally:
|
180
|
+
self.running_process = None
|
181
|
+
# Signal the server thread to stop
|
182
|
+
self.running = False
|
183
|
+
if self.thread:
|
184
|
+
self.thread.join(timeout=10) # Wait up to 10 seconds for thread to finish
|
185
|
+
if self.thread.is_alive():
|
186
|
+
print("Warning: Server thread did not terminate properly")
|
187
|
+
|
188
|
+
print("Compile server stopped")
|
189
|
+
|
190
|
+
def _server_loop(self) -> None:
|
191
|
+
try:
|
192
|
+
while self.running:
|
193
|
+
if self.running_process:
|
194
|
+
# Read Docker container output
|
195
|
+
# Check if Docker process is still running
|
196
|
+
if self.running_process.poll() is not None:
|
197
|
+
print("Docker server stopped unexpectedly")
|
198
|
+
self.running = False
|
199
|
+
break
|
200
|
+
|
201
|
+
time.sleep(0.1) # Prevent busy waiting
|
202
|
+
except KeyboardInterrupt:
|
203
|
+
print("Server thread stopped by user.")
|
204
|
+
except Exception as e:
|
205
|
+
print(f"Error in server thread: {e}")
|
206
|
+
finally:
|
207
|
+
self.running = False
|
@@ -210,16 +210,15 @@ class DockerManager:
|
|
210
210
|
def run_container(
|
211
211
|
self,
|
212
212
|
cmd: list[str],
|
213
|
-
volumes: dict[str,
|
213
|
+
volumes: dict[str, str] | None = None,
|
214
214
|
ports: dict[int, int] | None = None,
|
215
|
-
tty: bool = False,
|
216
215
|
) -> subprocess.Popen:
|
217
216
|
"""Run the Docker container with the specified volume.
|
218
217
|
|
219
218
|
Args:
|
220
|
-
|
221
|
-
|
222
|
-
|
219
|
+
volume_path: Path to the volume to mount
|
220
|
+
base_name: Base name for the mounted volume
|
221
|
+
build_mode: Build mode (DEBUG, QUICK, or RELEASE)
|
223
222
|
"""
|
224
223
|
volumes = volumes or {}
|
225
224
|
ports = ports or {}
|
@@ -227,8 +226,7 @@ class DockerManager:
|
|
227
226
|
print("Creating new container...")
|
228
227
|
docker_command = ["docker", "run"]
|
229
228
|
|
230
|
-
if
|
231
|
-
assert sys.stdout.isatty(), "TTY mode requires a TTY"
|
229
|
+
if sys.stdout.isatty():
|
232
230
|
docker_command.append("-it")
|
233
231
|
# Attach volumes if specified
|
234
232
|
docker_command += [
|
@@ -239,10 +237,8 @@ class DockerManager:
|
|
239
237
|
for host_port, container_port in ports.items():
|
240
238
|
docker_command.extend(["-p", f"{host_port}:{container_port}"])
|
241
239
|
if volumes:
|
242
|
-
for host_path,
|
243
|
-
docker_command.extend(
|
244
|
-
["-v", f"{host_path}:{mount_spec['bind']}:{mount_spec['mode']}"]
|
245
|
-
)
|
240
|
+
for host_path, container_path in volumes.items():
|
241
|
+
docker_command.extend(["-v", f"{host_path}:{container_path}"])
|
246
242
|
|
247
243
|
docker_command.extend(
|
248
244
|
[
|