fastled 1.2.33__py3-none-any.whl → 1.4.50__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.
Files changed (58) hide show
  1. fastled/__init__.py +51 -192
  2. fastled/__main__.py +14 -0
  3. fastled/__version__.py +6 -0
  4. fastled/app.py +124 -27
  5. fastled/args.py +124 -0
  6. fastled/assets/localhost-key.pem +28 -0
  7. fastled/assets/localhost.pem +27 -0
  8. fastled/cli.py +10 -2
  9. fastled/cli_test.py +21 -0
  10. fastled/cli_test_interactive.py +21 -0
  11. fastled/client_server.py +334 -55
  12. fastled/compile_server.py +12 -1
  13. fastled/compile_server_impl.py +115 -42
  14. fastled/docker_manager.py +392 -69
  15. fastled/emoji_util.py +27 -0
  16. fastled/filewatcher.py +100 -8
  17. fastled/find_good_connection.py +105 -0
  18. fastled/header_dump.py +63 -0
  19. fastled/install/__init__.py +1 -0
  20. fastled/install/examples_manager.py +62 -0
  21. fastled/install/extension_manager.py +113 -0
  22. fastled/install/main.py +156 -0
  23. fastled/install/project_detection.py +167 -0
  24. fastled/install/test_install.py +373 -0
  25. fastled/install/vscode_config.py +344 -0
  26. fastled/interruptible_http.py +148 -0
  27. fastled/keyboard.py +1 -0
  28. fastled/keyz.py +84 -0
  29. fastled/live_client.py +26 -1
  30. fastled/open_browser.py +133 -89
  31. fastled/parse_args.py +219 -15
  32. fastled/playwright/chrome_extension_downloader.py +207 -0
  33. fastled/playwright/playwright_browser.py +773 -0
  34. fastled/playwright/resize_tracking.py +127 -0
  35. fastled/print_filter.py +52 -0
  36. fastled/project_init.py +20 -13
  37. fastled/select_sketch_directory.py +142 -17
  38. fastled/server_flask.py +487 -0
  39. fastled/server_start.py +21 -0
  40. fastled/settings.py +53 -4
  41. fastled/site/build.py +2 -10
  42. fastled/site/examples.py +10 -0
  43. fastled/sketch.py +129 -7
  44. fastled/string_diff.py +218 -9
  45. fastled/test/examples.py +7 -5
  46. fastled/types.py +22 -2
  47. fastled/util.py +78 -0
  48. fastled/version.py +41 -0
  49. fastled/web_compile.py +401 -218
  50. fastled/zip_files.py +76 -0
  51. {fastled-1.2.33.dist-info → fastled-1.4.50.dist-info}/METADATA +533 -382
  52. fastled-1.4.50.dist-info/RECORD +60 -0
  53. {fastled-1.2.33.dist-info → fastled-1.4.50.dist-info}/WHEEL +1 -1
  54. fastled/open_browser2.py +0 -111
  55. fastled-1.2.33.dist-info/RECORD +0 -33
  56. {fastled-1.2.33.dist-info → fastled-1.4.50.dist-info}/entry_points.txt +0 -0
  57. {fastled-1.2.33.dist-info → fastled-1.4.50.dist-info/licenses}/LICENSE +0 -0
  58. {fastled-1.2.33.dist-info → fastled-1.4.50.dist-info}/top_level.txt +0 -0
@@ -1,6 +1,7 @@
1
1
  import subprocess
2
2
  import sys
3
3
  import time
4
+ import traceback
4
5
  import warnings
5
6
  from datetime import datetime, timezone
6
7
  from pathlib import Path
@@ -12,16 +13,23 @@ from fastled.docker_manager import (
12
13
  Container,
13
14
  DockerManager,
14
15
  RunningContainer,
16
+ Volume,
15
17
  )
18
+ from fastled.emoji_util import EMO
16
19
  from fastled.settings import DEFAULT_CONTAINER_NAME, IMAGE_NAME, SERVER_PORT
17
20
  from fastled.sketch import looks_like_fastled_repo
18
21
  from fastled.types import BuildMode, CompileResult, CompileServerError
22
+ from fastled.util import port_is_free, print_banner
19
23
 
20
24
  SERVER_OPTIONS = [
21
25
  "--allow-shutdown", # Allow the server to be shut down without a force kill.
22
26
  "--no-auto-update", # Don't auto live updates from the git repo.
23
27
  ]
24
28
 
29
+ LOCAL_DOCKER_ENV = {
30
+ "ONLY_QUICK_BUILDS": "0" # When running docker always allow release and debug.
31
+ }
32
+
25
33
 
26
34
  def _try_get_fastled_src(path: Path) -> Path | None:
27
35
  fastled_src_dir: Path | None = None
@@ -43,6 +51,8 @@ class CompileServerImpl:
43
51
  auto_start: bool = True,
44
52
  container_name: str | None = None,
45
53
  remove_previous: bool = False,
54
+ no_platformio: bool = False,
55
+ allow_libcompile: bool = True,
46
56
  ) -> None:
47
57
  container_name = container_name or DEFAULT_CONTAINER_NAME
48
58
  if interactive and not mapped_dir:
@@ -61,6 +71,18 @@ class CompileServerImpl:
61
71
  self.running_container: RunningContainer | None = None
62
72
  self.auto_updates = auto_updates
63
73
  self.remove_previous = remove_previous
74
+ self.no_platformio = no_platformio
75
+
76
+ # Guard: libfastled compilation requires volume source mapping
77
+ # If we don't have fastled_src_dir (not in FastLED repo), disable libcompile
78
+ if allow_libcompile and self.fastled_src_dir is None:
79
+ print(
80
+ f"{EMO('⚠️', 'WARNING:')} libfastled compilation disabled: volume source mapping not available"
81
+ )
82
+ print(" (not running in FastLED repository)")
83
+ allow_libcompile = False
84
+
85
+ self.allow_libcompile = allow_libcompile
64
86
  self._port = 0 # 0 until compile server is started
65
87
  if auto_start:
66
88
  self.start()
@@ -98,7 +120,12 @@ class CompileServerImpl:
98
120
  if not self.ping():
99
121
  raise RuntimeError("Server is not running")
100
122
  out: CompileResult = web_compile(
101
- directory, host=self.url(), build_mode=build_mode, profile=profile
123
+ directory,
124
+ host=self.url(),
125
+ build_mode=build_mode,
126
+ profile=profile,
127
+ no_platformio=self.no_platformio,
128
+ allow_libcompile=self.allow_libcompile,
102
129
  )
103
130
  return out
104
131
 
@@ -110,14 +137,24 @@ class CompileServerImpl:
110
137
  project_init(example=example, outputdir=outputdir)
111
138
 
112
139
  @property
113
- def running(self) -> bool:
140
+ def running(self) -> tuple[bool, Exception | None]:
114
141
  if not self._port:
115
- return False
142
+ return False, Exception("Docker hasn't been initialzed with a port yet")
116
143
  if not DockerManager.is_docker_installed():
117
- return False
118
- if not DockerManager.is_running():
119
- return False
120
- return self.docker.is_container_running(self.container_name)
144
+ return False, Exception("Docker is not installed")
145
+ docker_running, err = self.docker.is_running()
146
+ if not docker_running:
147
+ IS_MAC = sys.platform == "darwin"
148
+ if IS_MAC:
149
+ if "FileNotFoundError" in str(err):
150
+ traceback.print_exc()
151
+ print("\n\nNone fatal debug print for MacOS\n")
152
+ return False, err
153
+ ok: bool = self.docker.is_container_running(self.container_name)
154
+ if ok:
155
+ return True, None
156
+ else:
157
+ return False, Exception("Docker is not running")
121
158
 
122
159
  def using_fastled_src_dir_volume(self) -> bool:
123
160
  out = self.fastled_src_dir is not None
@@ -170,7 +207,10 @@ class CompileServerImpl:
170
207
  print("Compiling server starting")
171
208
 
172
209
  # Ensure Docker is running
173
- if not self.docker.is_running():
210
+ running: bool
211
+ # err: Exception | None
212
+ running, _ = self.docker.is_running()
213
+ if not running:
174
214
  if not self.docker.start():
175
215
  print("Docker could not be started. Exiting.")
176
216
  raise RuntimeError("Docker could not be started. Exiting.")
@@ -185,10 +225,11 @@ class CompileServerImpl:
185
225
  upgrade = True
186
226
  else:
187
227
  upgrade = self.auto_updates
188
- self.docker.validate_or_download_image(
189
- image_name=IMAGE_NAME, tag="main", upgrade=upgrade
228
+ updated = self.docker.validate_or_download_image(
229
+ image_name=IMAGE_NAME, tag="latest", upgrade=upgrade
190
230
  )
191
231
  DISK_CACHE.put("last-update", now_str)
232
+ INTERNAL_DOCKER_PORT = 80
192
233
 
193
234
  print("Docker image now validated")
194
235
  port = SERVER_PORT
@@ -196,71 +237,90 @@ class CompileServerImpl:
196
237
  server_command = ["/bin/bash"]
197
238
  else:
198
239
  server_command = ["python", "/js/run.py", "server"] + SERVER_OPTIONS
199
- ports = {80: port}
200
- volumes = None
240
+ if self.no_platformio:
241
+ server_command.append("--no-platformio")
242
+ if self.interactive:
243
+ print("Disabling port forwarding in interactive mode")
244
+ ports = {}
245
+ else:
246
+ ports = {INTERNAL_DOCKER_PORT: port}
247
+ volumes = []
201
248
  if self.fastled_src_dir:
202
- print(
203
- f"Mounting FastLED source directory {self.fastled_src_dir} into container /host/fastled/src"
249
+ msg = f"FastLED REPO updates enabled!!\n\nMounting FastLED source directory\n{self.fastled_src_dir} into container\n/host/fastled/src"
250
+ print_banner(msg)
251
+ volumes.append(
252
+ Volume(
253
+ host_path=str(self.fastled_src_dir),
254
+ container_path="/host/fastled/src",
255
+ mode="ro",
256
+ )
204
257
  )
205
- volumes = {
206
- str(self.fastled_src_dir): {"bind": "/host/fastled/src", "mode": "ro"}
207
- }
208
258
  if self.interactive:
209
259
  # add the mapped directory to the container
210
260
  print(f"Mounting {self.mapped_dir} into container /mapped")
211
- # volumes = {str(self.mapped_dir): {"bind": "/mapped", "mode": "rw"}}
212
- # add it
213
261
  assert self.mapped_dir is not None
214
262
  dir_name = self.mapped_dir.name
215
263
  if not volumes:
216
- volumes = {}
217
- volumes[str(self.mapped_dir)] = {
218
- "bind": f"/mapped/{dir_name}",
219
- "mode": "rw",
220
- }
264
+ volumes = []
265
+ volumes.append(
266
+ Volume(
267
+ host_path=str(self.mapped_dir),
268
+ container_path=f"/mapped/{dir_name}",
269
+ mode="rw",
270
+ )
271
+ )
221
272
  if self.fastled_src_dir is not None:
222
- # add volume for src/platforms/wasm/compiler/CMakelists.txt
223
273
  # to allow for interactive compilation
224
- interactive_sources = [
225
- "src/platforms/wasm/compiler/CMakeLists.txt",
226
- "src/platforms/wasm/compiler/build_archive.sh",
227
- ]
228
- for src in interactive_sources:
229
- src_path = Path(src).absolute()
274
+ # interactive_sources = list(INTERACTIVE_SOURCES)
275
+ interactive_sources: list[tuple[Path, str]] = []
276
+ src_host: Path
277
+ dst_container: str
278
+ for src_host, dst_container in interactive_sources:
279
+ src_path = Path(src_host).absolute()
230
280
  if src_path.exists():
231
- print(f"Mounting {src} into container")
232
- src_str = str(src_path)
233
- volumes[src_str] = {
234
- "bind": f"/js/fastled/{src}",
235
- "mode": "rw",
236
- }
281
+ print(f"Mounting {src_host} into container")
282
+ volumes.append(
283
+ Volume(
284
+ host_path=str(src_path),
285
+ container_path=dst_container,
286
+ mode="rw",
287
+ )
288
+ )
237
289
  else:
238
- print(f"Could not find {src}")
290
+ print(f"Could not find {src_path}")
239
291
 
240
292
  cmd_str = subprocess.list2cmdline(server_command)
241
293
  if not self.interactive:
242
294
  container: Container = self.docker.run_container_detached(
243
295
  image_name=IMAGE_NAME,
244
- tag="main",
296
+ tag="latest",
245
297
  container_name=self.container_name,
246
298
  command=cmd_str,
247
299
  ports=ports,
248
300
  volumes=volumes,
249
- remove_previous=self.interactive or self.remove_previous,
301
+ remove_previous=self.interactive or self.remove_previous or updated,
302
+ environment=LOCAL_DOCKER_ENV,
250
303
  )
251
304
  self.running_container = self.docker.attach_and_run(container)
252
305
  assert self.running_container is not None, "Container should be running"
253
306
  print("Compile server starting")
254
307
  return port
255
308
  else:
256
-
309
+ client_port_mapped = INTERNAL_DOCKER_PORT in ports
310
+ free_port = port_is_free(INTERNAL_DOCKER_PORT)
311
+ if client_port_mapped and free_port:
312
+ warnings.warn(
313
+ f"Can't expose port {INTERNAL_DOCKER_PORT}, disabling port forwarding in interactive mode"
314
+ )
315
+ ports = {}
257
316
  self.docker.run_container_interactive(
258
317
  image_name=IMAGE_NAME,
259
- tag="main",
318
+ tag="latest",
260
319
  container_name=self.container_name,
261
320
  command=cmd_str,
262
321
  ports=ports,
263
322
  volumes=volumes,
323
+ environment=LOCAL_DOCKER_ENV,
264
324
  )
265
325
 
266
326
  print("Exiting interactive mode")
@@ -270,9 +330,22 @@ class CompileServerImpl:
270
330
  return self.docker.is_container_running(self.container_name)
271
331
 
272
332
  def stop(self) -> None:
333
+ if self.docker.is_suspended:
334
+ return
273
335
  if self.running_container:
274
336
  self.running_container.detach()
275
337
  self.running_container = None
276
338
  self.docker.suspend_container(self.container_name)
277
339
  self._port = 0
278
340
  print("Compile server stopped")
341
+
342
+ def get_emsdk_headers(self, filepath: Path) -> None:
343
+ """Get EMSDK headers ZIP data from the server and save to filepath."""
344
+ from fastled.util import download_emsdk_headers
345
+
346
+ if not self._port:
347
+ raise RuntimeError("Server has not been started yet")
348
+ if not self.ping():
349
+ raise RuntimeError("Server is not running")
350
+
351
+ download_emsdk_headers(self.url(), filepath)