fastled 1.0.10__py2.py3-none-any.whl → 1.0.11__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/__init__.py CHANGED
@@ -1,3 +1,3 @@
1
1
  """FastLED Wasm Compiler package."""
2
2
 
3
- __version__ = "1.0.10"
3
+ __version__ = "1.0.11"
fastled/app.py CHANGED
@@ -17,7 +17,8 @@ from fastled import __version__
17
17
  from fastled.build_mode import BuildMode, get_build_mode
18
18
  from fastled.compile_server import CompileServer
19
19
  from fastled.docker_manager import DockerManager
20
- from fastled.filewatcher import create_file_watcher_process
20
+ from fastled.filewatcher import FileWatcherProcess
21
+ from fastled.keyboard import SpaceBarWatcher
21
22
  from fastled.open_browser import open_browser_thread
22
23
  from fastled.sketch import looks_like_sketch_directory
23
24
  from fastled.web_compile import web_compile
@@ -291,41 +292,66 @@ def run_client(args: argparse.Namespace) -> int:
291
292
  compile_server.stop()
292
293
  return 1
293
294
 
294
- # Watch mode
295
295
  print("\nWatching for changes. Press Ctrl+C to stop...")
296
- # watcher = FileChangedNotifier(args.directory, excluded_patterns=["fastled_js"])
297
- # watcher.start()
298
-
299
- from multiprocessing import Process, Queue
300
-
301
- proc: Process
302
- queue: Queue
303
- proc, queue = create_file_watcher_process(
296
+ sketch_filewatcher = FileWatcherProcess(
304
297
  args.directory, excluded_patterns=["fastled_js"]
305
298
  )
306
299
 
300
+ source_code_watcher: FileWatcherProcess | None = None
301
+ if compile_server and compile_server.using_fastled_src_dir_volume():
302
+ assert compile_server.fastled_src_dir is not None
303
+ source_code_watcher = FileWatcherProcess(
304
+ compile_server.fastled_src_dir, excluded_patterns=[]
305
+ )
306
+
307
+ def trigger_rebuild_if_sketch_changed(
308
+ last_compiled_result: CompiledResult,
309
+ ) -> CompiledResult:
310
+ changed_files = sketch_filewatcher.get_all_changes()
311
+ if changed_files:
312
+ print(f"\nChanges detected in {changed_files}")
313
+ last_hash_value = last_compiled_result.hash_value
314
+ out = compile_function(last_hash_value=last_hash_value)
315
+ if not out.success:
316
+ print("\nRecompilation failed.")
317
+ else:
318
+ print("\nRecompilation successful.")
319
+ return out
320
+ return last_compiled_result
321
+
307
322
  try:
308
323
  while True:
309
- try:
310
- size = queue.qsize()
311
- changed_files = []
312
- for i in range(size):
313
- changed_files.append(queue.get())
314
- except KeyboardInterrupt:
315
- print("\nExiting from watcher...")
316
- raise
317
- except Exception as e:
318
- print(f"Error getting changes: {e}")
319
- changed_files = []
320
- if changed_files:
321
- print(f"\nChanges detected in {changed_files}")
322
- last_hash_value = last_compiled_result.hash_value
323
- result = compile_function(last_hash_value=last_hash_value)
324
- if not result.success:
325
- print("\nRecompilation failed.")
326
- else:
327
- print("\nRecompilation successful.")
328
- time.sleep(0.3)
324
+ last_compiled_result = trigger_rebuild_if_sketch_changed(
325
+ last_compiled_result
326
+ )
327
+ if compile_server and not compile_server.proceess_running():
328
+ print("Server process is not running. Exiting...")
329
+ return 1
330
+ if source_code_watcher is not None:
331
+ changed_files = source_code_watcher.get_all_changes()
332
+ if changed_files:
333
+ print(f"\nChanges detected in FastLED source code: {changed_files}")
334
+ print("Press space bar to trigger compile.")
335
+
336
+ space_key_watcher = SpaceBarWatcher()
337
+ try:
338
+ while True:
339
+ if space_key_watcher.space_bar_pressed():
340
+ print("Space bar pressed, triggering recompile...")
341
+ last_compiled_result = compile_function(
342
+ last_hash_value=None
343
+ )
344
+ print("Finished recompile.")
345
+ break
346
+ elif len(sketch_filewatcher.get_all_changes()) > 0:
347
+ last_compiled_result = compile_function(
348
+ last_hash_value=None
349
+ )
350
+ break
351
+ time.sleep(0.1)
352
+ finally:
353
+ space_key_watcher.stop()
354
+
329
355
  except KeyboardInterrupt:
330
356
  print("\nStopping watch mode...")
331
357
  return 0
@@ -333,7 +359,7 @@ def run_client(args: argparse.Namespace) -> int:
333
359
  print(f"Error: {e}")
334
360
  return 1
335
361
  finally:
336
- proc.terminate()
362
+ sketch_filewatcher.stop()
337
363
  if compile_server:
338
364
  compile_server.stop()
339
365
  if browser_proc:
@@ -372,8 +398,8 @@ def main() -> int:
372
398
 
373
399
  if __name__ == "__main__":
374
400
  try:
401
+ os.chdir("../fastled")
375
402
  sys.argv.append("examples/SdCard")
376
- sys.argv.append("--local")
377
403
  sys.exit(main())
378
404
  except KeyboardInterrupt:
379
405
  print("\nExiting from main...")
fastled/compile_server.py CHANGED
@@ -1,251 +1,216 @@
1
- import socket
2
- import subprocess
3
- import threading
4
- import time
5
- from pathlib import Path
6
- from typing import Optional
7
-
8
- import httpx
9
-
10
- from fastled.docker_manager import DockerManager
11
- from fastled.sketch import looks_like_fastled_repo
12
-
13
- _DEFAULT_CONTAINER_NAME = "fastled-wasm-compiler"
14
-
15
- SERVER_PORT = 9021
16
-
17
-
18
- def find_available_port(start_port: int = SERVER_PORT) -> int:
19
- """Find an available port starting from the given port."""
20
- port = start_port
21
- end_port = start_port + 1000
22
- while True:
23
- with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
24
- if s.connect_ex(("localhost", port)) != 0:
25
- return port
26
- port += 1
27
- if port >= end_port:
28
- raise RuntimeError("No available ports found")
29
-
30
-
31
- class CompileServer:
32
- def __init__(
33
- self,
34
- container_name=_DEFAULT_CONTAINER_NAME,
35
- interactive: bool = False,
36
- ) -> None:
37
-
38
- cwd = Path(".").resolve()
39
- fastled_src_dir: Path | None = None
40
- if looks_like_fastled_repo(cwd):
41
- print(
42
- "Looks like a FastLED repo, using it as the source directory and mapping it into the server."
43
- )
44
- fastled_src_dir = cwd / "src"
45
-
46
- self.container_name = container_name
47
- self.docker = DockerManager(container_name=container_name)
48
- self.running = False
49
- self.thread: Optional[threading.Thread] = None
50
- self.running_process: subprocess.Popen | None = None
51
- self.fastled_src_dir: Path | None = fastled_src_dir
52
- self.interactive = interactive
53
- self._port = self._start()
54
-
55
- def port(self) -> int:
56
- return self._port
57
-
58
- def url(self) -> str:
59
- return f"http://localhost:{self._port}"
60
-
61
- def wait_for_startup(self, timeout: int = 100) -> bool:
62
- """Wait for the server to start up."""
63
- start_time = time.time()
64
- while time.time() - start_time < timeout:
65
- # ping the server to see if it's up
66
- if not self._port:
67
- return False
68
- # use httpx to ping the server
69
- # if successful, return True
70
- try:
71
- response = httpx.get(
72
- f"http://localhost:{self._port}", follow_redirects=True
73
- )
74
- if response.status_code < 400:
75
- return True
76
- except KeyboardInterrupt:
77
- raise
78
- except Exception:
79
- pass
80
- time.sleep(0.1)
81
- if not self.running:
82
- return False
83
- return False
84
-
85
- def _start(self) -> int:
86
- print("Compiling server starting")
87
- self.running = True
88
- # Ensure Docker is running
89
- with self.docker.get_lock():
90
- if not self.docker.is_running():
91
- if not self.docker.start():
92
- print("Docker could not be started. Exiting.")
93
- raise RuntimeError("Docker could not be started. Exiting.")
94
-
95
- # Clean up any existing container with the same name
96
-
97
- try:
98
- container_exists = (
99
- subprocess.run(
100
- ["docker", "inspect", self.container_name],
101
- capture_output=True,
102
- text=True,
103
- ).returncode
104
- == 0
105
- )
106
- if container_exists:
107
- print("Cleaning up existing container")
108
- subprocess.run(
109
- ["docker", "rm", "-f", self.container_name],
110
- check=False,
111
- )
112
- except KeyboardInterrupt:
113
- raise
114
- except Exception as e:
115
- print(f"Warning: Failed to remove existing container: {e}")
116
-
117
- print("Ensuring Docker image exists at latest version")
118
- if not self.docker.ensure_image_exists():
119
- print("Failed to ensure Docker image exists.")
120
- raise RuntimeError("Failed to ensure Docker image exists")
121
-
122
- print("Docker image now validated")
123
-
124
- # Remove the image to force a fresh download
125
- # subprocess.run(["docker", "rmi", "fastled-wasm"], capture_output=True)
126
- # print("All clean")
127
-
128
- port = find_available_port()
129
- print(f"Found an available port: {port}")
130
- # server_command = ["python", "/js/run.py", "server", "--allow-shutdown"]
131
- if self.interactive:
132
- server_command = ["/bin/bash"]
133
- else:
134
- server_command = ["python", "/js/run.py", "server"]
135
- print(f"Started Docker container with command: {server_command}")
136
- ports = {port: 80}
137
- volumes = None
138
- if self.fastled_src_dir:
139
- print(
140
- f"Mounting FastLED source directory {self.fastled_src_dir} into container /host/fastled/src"
141
- )
142
- volumes = {
143
- str(self.fastled_src_dir): {"bind": "/host/fastled/src", "mode": "ro"}
144
- }
145
- if not self.interactive:
146
- # no auto-update because the source directory is mapped in.
147
- # This should be automatic now.
148
- server_command.append("--no-auto-update") # stop git repo updates.
149
- self.running_process = self.docker.run_container(
150
- server_command, ports=ports, volumes=volumes
151
- )
152
- print("Compile server starting")
153
- time.sleep(3)
154
- if self.running_process.poll() is not None:
155
- print("Server failed to start")
156
- self.running = False
157
- raise RuntimeError("Server failed to start")
158
- self.thread = threading.Thread(target=self._server_loop, daemon=True)
159
- self.thread.start()
160
-
161
- return port
162
-
163
- def proceess_running(self) -> bool:
164
- if self.running_process is None:
165
- return False
166
- return self.running_process.poll() is None
167
-
168
- def stop(self) -> None:
169
- print(f"Stopping server on port {self._port}")
170
- # attempt to send a shutdown signal to the server
171
- try:
172
- httpx.get(f"http://localhost:{self._port}/shutdown", timeout=2)
173
- # except Exception:
174
- except Exception as e:
175
- print(f"Failed to send shutdown signal: {e}")
176
- pass
177
- try:
178
- # Stop the Docker container
179
- cp: subprocess.CompletedProcess
180
- cp = subprocess.run(
181
- ["docker", "stop", self.container_name],
182
- capture_output=True,
183
- text=True,
184
- check=True,
185
- )
186
- if cp.returncode != 0:
187
- print(f"Failed to stop Docker container: {cp.stderr}")
188
-
189
- cp = subprocess.run(
190
- ["docker", "rm", self.container_name],
191
- capture_output=True,
192
- text=True,
193
- check=True,
194
- )
195
- if cp.returncode != 0:
196
- print(f"Failed to remove Docker container: {cp.stderr}")
197
-
198
- # Close the stdout pipe
199
- if self.running_process and self.running_process.stdout:
200
- self.running_process.stdout.close()
201
-
202
- # Wait for the process to fully terminate with a timeout
203
- self.running_process.wait(timeout=10)
204
- if self.running_process.returncode is None:
205
- # kill
206
- self.running_process.kill()
207
- if self.running_process.returncode is not None:
208
- print(
209
- f"Server stopped with return code {self.running_process.returncode}"
210
- )
211
-
212
- except subprocess.TimeoutExpired:
213
- # Force kill if it doesn't stop gracefully
214
- if self.running_process:
215
- self.running_process.kill()
216
- self.running_process.wait()
217
- except KeyboardInterrupt:
218
- if self.running_process:
219
- self.running_process.kill()
220
- self.running_process.wait()
221
- except Exception as e:
222
- print(f"Error stopping Docker container: {e}")
223
- finally:
224
- self.running_process = None
225
- # Signal the server thread to stop
226
- self.running = False
227
- if self.thread:
228
- self.thread.join(timeout=10) # Wait up to 10 seconds for thread to finish
229
- if self.thread.is_alive():
230
- print("Warning: Server thread did not terminate properly")
231
-
232
- print("Compile server stopped")
233
-
234
- def _server_loop(self) -> None:
235
- try:
236
- while self.running:
237
- if self.running_process:
238
- # Read Docker container output
239
- # Check if Docker process is still running
240
- if self.running_process.poll() is not None:
241
- print("Docker server stopped unexpectedly")
242
- self.running = False
243
- break
244
-
245
- time.sleep(0.1) # Prevent busy waiting
246
- except KeyboardInterrupt:
247
- print("Server thread stopped by user.")
248
- except Exception as e:
249
- print(f"Error in server thread: {e}")
250
- finally:
251
- self.running = False
1
+ import socket
2
+ import subprocess
3
+ import time
4
+ from pathlib import Path
5
+
6
+ import httpx
7
+
8
+ from fastled.docker_manager import DockerManager
9
+ from fastled.sketch import looks_like_fastled_repo
10
+
11
+ _DEFAULT_CONTAINER_NAME = "fastled-wasm-compiler"
12
+
13
+ SERVER_PORT = 9021
14
+
15
+
16
+ def find_available_port(start_port: int = SERVER_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,
32
+ container_name=_DEFAULT_CONTAINER_NAME,
33
+ interactive: bool = False,
34
+ ) -> None:
35
+
36
+ cwd = Path(".").resolve()
37
+ fastled_src_dir: Path | None = None
38
+ if looks_like_fastled_repo(cwd):
39
+ print(
40
+ "Looks like a FastLED repo, using it as the source directory and mapping it into the server."
41
+ )
42
+ fastled_src_dir = cwd / "src"
43
+
44
+ self.container_name = container_name
45
+ self.docker = DockerManager(container_name=container_name)
46
+ self.running = False
47
+ self.running_process: subprocess.Popen | None = None
48
+ self.fastled_src_dir: Path | None = fastled_src_dir
49
+ self.interactive = interactive
50
+ self._port = self._start()
51
+
52
+ def using_fastled_src_dir_volume(self) -> bool:
53
+ return self.fastled_src_dir is not None
54
+
55
+ def port(self) -> int:
56
+ return self._port
57
+
58
+ def url(self) -> str:
59
+ return f"http://localhost:{self._port}"
60
+
61
+ def wait_for_startup(self, timeout: int = 100) -> bool:
62
+ """Wait for the server to start up."""
63
+ start_time = time.time()
64
+ while time.time() - start_time < timeout:
65
+ # ping the server to see if it's up
66
+ if not self._port:
67
+ return False
68
+ # use httpx to ping the server
69
+ # if successful, return True
70
+ try:
71
+ response = httpx.get(
72
+ f"http://localhost:{self._port}", follow_redirects=True
73
+ )
74
+ if response.status_code < 400:
75
+ return True
76
+ except KeyboardInterrupt:
77
+ raise
78
+ except Exception:
79
+ pass
80
+ time.sleep(0.1)
81
+ if not self.running:
82
+ return False
83
+ return False
84
+
85
+ def _start(self) -> int:
86
+ print("Compiling server starting")
87
+ self.running = True
88
+ # Ensure Docker is running
89
+ with self.docker.get_lock():
90
+ if not self.docker.is_running():
91
+ if not self.docker.start():
92
+ print("Docker could not be started. Exiting.")
93
+ raise RuntimeError("Docker could not be started. Exiting.")
94
+
95
+ # Clean up any existing container with the same name
96
+ try:
97
+ container_exists = (
98
+ subprocess.run(
99
+ ["docker", "inspect", self.container_name],
100
+ capture_output=True,
101
+ text=True,
102
+ ).returncode
103
+ == 0
104
+ )
105
+ if container_exists:
106
+ print("Cleaning up existing container")
107
+ subprocess.run(
108
+ ["docker", "rm", "-f", self.container_name],
109
+ check=False,
110
+ )
111
+ except KeyboardInterrupt:
112
+ raise
113
+ except Exception as e:
114
+ print(f"Warning: Failed to remove existing container: {e}")
115
+
116
+ print("Ensuring Docker image exists at latest version")
117
+ if not self.docker.ensure_image_exists():
118
+ print("Failed to ensure Docker image exists.")
119
+ raise RuntimeError("Failed to ensure Docker image exists")
120
+
121
+ print("Docker image now validated")
122
+ port = find_available_port()
123
+ print(f"Found an available port: {port}")
124
+ if self.interactive:
125
+ server_command = ["/bin/bash"]
126
+ else:
127
+ server_command = ["python", "/js/run.py", "server", "--allow-shutdown"]
128
+ print(f"Started Docker container with command: {server_command}")
129
+ ports = {port: 80}
130
+ volumes = None
131
+ if self.fastled_src_dir:
132
+ print(
133
+ f"Mounting FastLED source directory {self.fastled_src_dir} into container /host/fastled/src"
134
+ )
135
+ volumes = {
136
+ str(self.fastled_src_dir): {"bind": "/host/fastled/src", "mode": "ro"}
137
+ }
138
+ if not self.interactive:
139
+ # no auto-update because the source directory is mapped in.
140
+ # This should be automatic now.
141
+ server_command.append("--no-auto-update") # stop git repo updates.
142
+ self.running_process = self.docker.run_container(
143
+ server_command, ports=ports, volumes=volumes, tty=self.interactive
144
+ )
145
+ print("Compile server starting")
146
+ time.sleep(3)
147
+ if self.running_process.poll() is not None:
148
+ print("Server failed to start")
149
+ self.running = False
150
+ raise RuntimeError("Server failed to start")
151
+ return port
152
+
153
+ def proceess_running(self) -> bool:
154
+ if self.running_process is None:
155
+ return False
156
+ return self.running_process.poll() is None
157
+
158
+ def stop(self) -> None:
159
+ print(f"Stopping server on port {self._port}")
160
+ # # attempt to send a shutdown signal to the server
161
+ # try:
162
+ # httpx.get(f"http://localhost:{self._port}/shutdown", timeout=2)
163
+ # # except Exception:
164
+ # except Exception as e:
165
+ # print(f"Failed to send shutdown signal: {e}")
166
+ # pass
167
+ try:
168
+ # Stop the Docker container
169
+ cp: subprocess.CompletedProcess
170
+ cp = subprocess.run(
171
+ ["docker", "stop", self.container_name],
172
+ capture_output=True,
173
+ text=True,
174
+ check=True,
175
+ )
176
+ if cp.returncode != 0:
177
+ print(f"Failed to stop Docker container: {cp.stderr}")
178
+
179
+ cp = subprocess.run(
180
+ ["docker", "rm", self.container_name],
181
+ capture_output=True,
182
+ text=True,
183
+ check=True,
184
+ )
185
+ if cp.returncode != 0:
186
+ print(f"Failed to remove Docker container: {cp.stderr}")
187
+
188
+ # Close the stdout pipe
189
+ if self.running_process and self.running_process.stdout:
190
+ self.running_process.stdout.close()
191
+
192
+ # Wait for the process to fully terminate with a timeout
193
+ self.running_process.wait(timeout=10)
194
+ if self.running_process.returncode is None:
195
+ # kill
196
+ self.running_process.kill()
197
+ if self.running_process.returncode is not None:
198
+ print(
199
+ f"Server stopped with return code {self.running_process.returncode}"
200
+ )
201
+
202
+ except subprocess.TimeoutExpired:
203
+ # Force kill if it doesn't stop gracefully
204
+ if self.running_process:
205
+ self.running_process.kill()
206
+ self.running_process.wait()
207
+ except KeyboardInterrupt:
208
+ if self.running_process:
209
+ self.running_process.kill()
210
+ self.running_process.wait()
211
+ except Exception as e:
212
+ print(f"Error stopping Docker container: {e}")
213
+ finally:
214
+ self.running_process = None
215
+ self.running = False
216
+ print("Compile server stopped")
fastled/docker_manager.py CHANGED
@@ -212,6 +212,7 @@ class DockerManager:
212
212
  cmd: list[str],
213
213
  volumes: dict[str, dict[str, str]] | None = None,
214
214
  ports: dict[int, int] | None = None,
215
+ tty: bool = False,
215
216
  ) -> subprocess.Popen:
216
217
  """Run the Docker container with the specified volume.
217
218
 
@@ -226,7 +227,8 @@ class DockerManager:
226
227
  print("Creating new container...")
227
228
  docker_command = ["docker", "run"]
228
229
 
229
- if sys.stdout.isatty():
230
+ if tty:
231
+ assert sys.stdout.isatty(), "TTY mode requires a TTY"
230
232
  docker_command.append("-it")
231
233
  # Attach volumes if specified
232
234
  docker_command += [
fastled/filewatcher.py CHANGED
@@ -2,11 +2,14 @@
2
2
  """
3
3
 
4
4
  import hashlib
5
+ import os
5
6
  import queue
6
7
  import threading
7
8
  import time
9
+ from contextlib import redirect_stdout
8
10
  from multiprocessing import Process, Queue
9
11
  from pathlib import Path
12
+ from queue import Empty
10
13
  from typing import Dict, Set
11
14
 
12
15
  from watchdog.events import FileSystemEvent, FileSystemEventHandler
@@ -148,22 +151,46 @@ class FileChangedNotifier(threading.Thread):
148
151
 
149
152
 
150
153
  def _process_wrapper(root: Path, excluded_patterns: list[str], queue: Queue):
151
- watcher = FileChangedNotifier(str(root), excluded_patterns=excluded_patterns)
152
- watcher.start()
153
- while True:
154
- try:
155
- changed_files = watcher.get_all_changes()
156
- for file in changed_files:
157
- queue.put(file)
158
- except KeyboardInterrupt:
159
- break
160
- watcher.stop()
154
+ with open(os.devnull, "w") as fnull: # Redirect to /dev/null
155
+ with redirect_stdout(fnull):
156
+ watcher = FileChangedNotifier(
157
+ str(root), excluded_patterns=excluded_patterns
158
+ )
159
+ watcher.start()
160
+ while True:
161
+ try:
162
+ changed_files = watcher.get_all_changes()
163
+ for file in changed_files:
164
+ queue.put(file)
165
+ except KeyboardInterrupt:
166
+ break
167
+ watcher.stop()
168
+
169
+
170
+ class FileWatcherProcess:
171
+ def __init__(self, root: Path, excluded_patterns: list[str]) -> None:
172
+ self.queue: Queue = Queue()
173
+ self.process = Process(
174
+ target=_process_wrapper,
175
+ args=(root, excluded_patterns, self.queue),
176
+ daemon=True,
177
+ )
178
+ self.process.start()
179
+
180
+ def stop(self):
181
+ self.process.terminate()
182
+ self.process.join()
183
+ self.queue.close()
184
+ self.queue.join_thread()
161
185
 
186
+ def get_all_changes(self, timeout: float | None = None) -> list[str]:
187
+ changed_files = []
188
+ block = timeout is not None
162
189
 
163
- def create_file_watcher_process(
164
- root: Path, excluded_patterns: list[str]
165
- ) -> tuple[Process, Queue]:
166
- """Create a new process running the file watcher"""
167
- queue: Queue = Queue()
168
- process = Process(target=_process_wrapper, args=(root, excluded_patterns, queue))
169
- return process, queue
190
+ while True:
191
+ try:
192
+ changed_file = self.queue.get(block=block, timeout=timeout)
193
+ changed_files.append(changed_file)
194
+ except Empty:
195
+ break
196
+ return changed_files
fastled/keyboard.py ADDED
@@ -0,0 +1,89 @@
1
+ import os
2
+ import select
3
+ import sys
4
+ import time
5
+ from multiprocessing import Process, Queue
6
+ from queue import Empty
7
+
8
+
9
+ class SpaceBarWatcher:
10
+ def __init__(self) -> None:
11
+ self.queue: Queue = Queue()
12
+ self.queue_cancel: Queue = Queue()
13
+ self.process = Process(target=self._watch_for_space)
14
+ self.process.start()
15
+
16
+ def _watch_for_space(self) -> None:
17
+ # Set stdin to non-blocking mode
18
+ fd = sys.stdin.fileno()
19
+ if os.name != "nt": # Unix-like systems
20
+ import termios
21
+ import tty
22
+
23
+ old_settings = termios.tcgetattr(fd) # type: ignore
24
+ try:
25
+ tty.setraw(fd) # type: ignore
26
+ while True:
27
+ # Check for cancel signal
28
+ try:
29
+ self.queue_cancel.get(timeout=0.1)
30
+ break
31
+ except Empty:
32
+ pass
33
+
34
+ # Check if there's input ready
35
+ if select.select([sys.stdin], [], [], 0.1)[0]:
36
+ char = sys.stdin.read(1)
37
+ if char == " ":
38
+ self.queue.put(ord(" "))
39
+ finally:
40
+ termios.tcsetattr(fd, termios.TCSADRAIN, old_settings) # type: ignore
41
+ else: # Windows
42
+ import msvcrt
43
+
44
+ while True:
45
+ # Check for cancel signal
46
+ try:
47
+ self.queue_cancel.get(timeout=0.1)
48
+ break
49
+ except Empty:
50
+ pass
51
+
52
+ # Check if there's input ready
53
+ if msvcrt.kbhit():
54
+ char = msvcrt.getch().decode()
55
+ if char == " ":
56
+ self.queue.put(ord(" "))
57
+
58
+ def space_bar_pressed(self) -> bool:
59
+ found = False
60
+ while not self.queue.empty():
61
+ key = self.queue.get()
62
+ if key == ord(" "):
63
+ found = True
64
+ return found
65
+
66
+ def stop(self) -> None:
67
+ self.queue_cancel.put(True)
68
+ self.process.terminate()
69
+ self.process.join()
70
+ self.queue.close()
71
+ self.queue.join_thread()
72
+
73
+
74
+ def main() -> None:
75
+ watcher = SpaceBarWatcher()
76
+ try:
77
+ while True:
78
+ if watcher.space_bar_pressed():
79
+ break
80
+ time.sleep(1)
81
+ finally:
82
+ watcher.stop()
83
+
84
+
85
+ if __name__ == "__main__":
86
+ try:
87
+ main()
88
+ except KeyboardInterrupt:
89
+ print("Keyboard interrupt detected.")
fastled/open_browser.py CHANGED
@@ -24,7 +24,11 @@ def _open_browser_python(fastled_js: Path, port: int) -> subprocess.Popen:
24
24
  ]
25
25
 
26
26
  # Start the process
27
- process = subprocess.Popen(cmd)
27
+ process = subprocess.Popen(
28
+ cmd,
29
+ # stdout=subprocess.DEVNULL,
30
+ stderr=subprocess.DEVNULL,
31
+ )
28
32
  return process
29
33
 
30
34
 
fastled/web_compile.py CHANGED
@@ -4,7 +4,7 @@ import os
4
4
  import shutil
5
5
  import tempfile
6
6
  import zipfile
7
- from concurrent.futures import ThreadPoolExecutor, as_completed
7
+ from concurrent.futures import ProcessPoolExecutor, as_completed
8
8
  from dataclasses import dataclass
9
9
  from pathlib import Path
10
10
 
@@ -20,7 +20,7 @@ ENDPOINT_COMPILED_WASM = "compile/wasm"
20
20
  _TIMEOUT = 60 * 4 # 2 mins timeout
21
21
  _AUTH_TOKEN = "oBOT5jbsO4ztgrpNsQwlmFLIKB"
22
22
  ENABLE_EMBEDDED_DATA = True
23
- _THREAD_POOL = ThreadPoolExecutor(max_workers=8)
23
+ _EXECUTOR = ProcessPoolExecutor(max_workers=8)
24
24
 
25
25
 
26
26
  @dataclass
@@ -161,7 +161,7 @@ def web_compile(
161
161
  ip_versions = [True, False] if "localhost" not in host else [True]
162
162
  for ipv4 in ip_versions:
163
163
  for url in urls:
164
- f = _THREAD_POOL.submit(_test_connection, url, ipv4)
164
+ f = _EXECUTOR.submit(_test_connection, url, ipv4)
165
165
  futures.append(f)
166
166
 
167
167
  succeeded = False
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: fastled
3
- Version: 1.0.10
3
+ Version: 1.0.11
4
4
  Summary: FastLED Wasm Compiler
5
5
  Home-page: https://github.com/zackees/fastled-wasm
6
6
  Maintainer: Zachary Vorhies
@@ -16,6 +16,7 @@ Requires-Dist: watchdog
16
16
  Requires-Dist: livereload
17
17
  Requires-Dist: download
18
18
  Requires-Dist: filelock
19
+ Requires-Dist: windows-curses; platform_system == "Windows"
19
20
 
20
21
  # FastLED wasm compiler
21
22
 
@@ -93,6 +94,7 @@ provide shims for most of the common api points.
93
94
 
94
95
  # Revisions
95
96
 
97
+ * 1.1.11 - Dev improvement: FastLED src code volume mapped into docker will just in time update without having to manually trigger it.
96
98
  * 1.1.10 - Swap large assets with embedded placeholders. This helps video sketches upload and compile instantly. Assets are re-added on after compile artifacts are returned.
97
99
  * 1.1.9 - Remove auto server and instead tell the user corrective action to take.
98
100
  * 1.1.8 - Program now knows it's own version which will be displayed with help file. Use `--version` to get it directly.
@@ -0,0 +1,20 @@
1
+ fastled/__init__.py,sha256=f3KhION06nm0tJFF8POOIXhKebdmWIMlx8mVEmVFUrw,64
2
+ fastled/app.py,sha256=WpcXlNUJzMPmH6BZIK6S15SGiAWtQmXungxlw0uOooc,13955
3
+ fastled/build_mode.py,sha256=joMwsV4K1y_LijT4gEAcjx69RZBoe_KmFmHZdPYbL_4,631
4
+ fastled/cli.py,sha256=CNR_pQR0sNVPNuv8e_nmm-0PI8sU-eUBUgnWgWkzW9c,237
5
+ fastled/compile_server.py,sha256=-Qd9x0l-KfiFRGqZYOi3BRD7iEqfJmJU6EgFY-QJodg,7919
6
+ fastled/docker_manager.py,sha256=gaDZpFV7E-9JhYIn6ahkP--9dGT32-WR5wZaU-o--6g,9107
7
+ fastled/filewatcher.py,sha256=fJNMQRDCpihSL4nQeYPqbD4m1Jzjcz_-YRAo-wlPW6k,6518
8
+ fastled/keyboard.py,sha256=PiAUhga8R_crMqFQRwjKyg6bgVTMlpezWa0gaGmL_So,2542
9
+ fastled/open_browser.py,sha256=RRHcsZ5Vzsw1AuZUEYuSfjKmf_9j3NGMDUR-FndHmqs,1483
10
+ fastled/paths.py,sha256=VsPmgu0lNSCFOoEC0BsTYzDygXqy15AHUfN-tTuzDZA,99
11
+ fastled/sketch.py,sha256=KhhPFqlFVlBk8YrzFy7-ioe7zEzecgrVLhyFbLpBp7k,1845
12
+ fastled/util.py,sha256=t4M3NFMhnCzfYbLvIyJi0RdFssZqbTN_vVIaej1WV-U,265
13
+ fastled/web_compile.py,sha256=jN9p-ODWm_EspFlsT4M7SfyI_wAdi17u7_JEIef5WVw,9645
14
+ fastled/assets/example.txt,sha256=lTBovRjiz0_TgtAtbA1C5hNi2ffbqnNPqkKg6UiKCT8,54
15
+ fastled-1.0.11.dist-info/LICENSE,sha256=b6pOoifSXiUaz_lDS84vWlG3fr4yUKwB8fzkrH9R8bQ,1064
16
+ fastled-1.0.11.dist-info/METADATA,sha256=vi_VLpN90hbv8hxpGib2nRtRTYj5GBnoakQccKh7iuY,6780
17
+ fastled-1.0.11.dist-info/WHEEL,sha256=0VNUDWQJzfRahYI3neAhz2UVbRCtztpN5dPHAGvmGXc,109
18
+ fastled-1.0.11.dist-info/entry_points.txt,sha256=RCwmzCSOS4-C2i9EziANq7Z2Zb4KFnEMR1FQC0bBwAw,101
19
+ fastled-1.0.11.dist-info/top_level.txt,sha256=xfG6Z_ol9V5YmBROkZq2QTRwjbS2ouCUxaTJsOwfkOo,14
20
+ fastled-1.0.11.dist-info/RECORD,,
@@ -1,34 +0,0 @@
1
- from pygments import lex
2
- from pygments.lexers import CppLexer
3
- from pygments.token import Token
4
-
5
-
6
- def check_cpp_syntax(code):
7
- try:
8
- # Tokenize the code to check for basic syntax issues
9
- for token_type, token_value in lex(code, CppLexer()):
10
- if token_type == Token.Error:
11
- print(f"Syntax error detected: {token_value}")
12
- return False
13
- print("No syntax errors detected.")
14
- return True
15
- except Exception as e:
16
- print(f"Error during syntax check: {e}")
17
- return False
18
-
19
-
20
- def main():
21
- file_path = input("Enter the path to your C++ file: ")
22
- try:
23
- with open(file_path, "r") as file:
24
- code = file.read()
25
- if check_cpp_syntax(code):
26
- print("The file can now be sent to the server.")
27
- else:
28
- print("Please fix the syntax errors before sending.")
29
- except FileNotFoundError:
30
- print("File not found. Please check the path and try again.")
31
-
32
-
33
- if __name__ == "__main__":
34
- main()
@@ -1,20 +0,0 @@
1
- fastled/__init__.py,sha256=8Zhmsg5KFQ2ogDbb2OFvaS5FPnsi76DJDmIxJh6z5Yg,64
2
- fastled/app.py,sha256=cRLPDB_GR1WkRNbmHPyyDiG4DAqpiB6MpFIyfa33_c4,12522
3
- fastled/build_mode.py,sha256=joMwsV4K1y_LijT4gEAcjx69RZBoe_KmFmHZdPYbL_4,631
4
- fastled/check_cpp_syntax.py,sha256=YxRJm7cFPv4bdhL1v_KOkBz8RL86ihayoJYvclr69ms,1024
5
- fastled/cli.py,sha256=CNR_pQR0sNVPNuv8e_nmm-0PI8sU-eUBUgnWgWkzW9c,237
6
- fastled/compile_server.py,sha256=8avG0mAjdjU0w1ej4LiKVGLgkHi8dZxT48TLuGecj7A,9451
7
- fastled/docker_manager.py,sha256=WcOKa3EpIPAjICPfTL87CUYuAmX9KYT6L_Hcqbj95eE,9028
8
- fastled/filewatcher.py,sha256=gwxNINttXQXJk3P4CpCav3-UwO3cbCg9k0ySGWOQYh0,5686
9
- fastled/open_browser.py,sha256=-VhpGmydwLCcXmrDD2esMEdJPZYcoX2Mt73eb88Nna0,1392
10
- fastled/paths.py,sha256=VsPmgu0lNSCFOoEC0BsTYzDygXqy15AHUfN-tTuzDZA,99
11
- fastled/sketch.py,sha256=KhhPFqlFVlBk8YrzFy7-ioe7zEzecgrVLhyFbLpBp7k,1845
12
- fastled/util.py,sha256=t4M3NFMhnCzfYbLvIyJi0RdFssZqbTN_vVIaej1WV-U,265
13
- fastled/web_compile.py,sha256=Wl06e4nTAEXcn7N03eM0qLsMy4dPv6_hyZ4r7zyJJ1A,9649
14
- fastled/assets/example.txt,sha256=lTBovRjiz0_TgtAtbA1C5hNi2ffbqnNPqkKg6UiKCT8,54
15
- fastled-1.0.10.dist-info/LICENSE,sha256=b6pOoifSXiUaz_lDS84vWlG3fr4yUKwB8fzkrH9R8bQ,1064
16
- fastled-1.0.10.dist-info/METADATA,sha256=XmFMC3Pd1YlhemfA_aGSSetoNLvWnD4YFtOuPsxpRT4,6581
17
- fastled-1.0.10.dist-info/WHEEL,sha256=0VNUDWQJzfRahYI3neAhz2UVbRCtztpN5dPHAGvmGXc,109
18
- fastled-1.0.10.dist-info/entry_points.txt,sha256=RCwmzCSOS4-C2i9EziANq7Z2Zb4KFnEMR1FQC0bBwAw,101
19
- fastled-1.0.10.dist-info/top_level.txt,sha256=xfG6Z_ol9V5YmBROkZq2QTRwjbS2ouCUxaTJsOwfkOo,14
20
- fastled-1.0.10.dist-info/RECORD,,