fastled 1.2.66__tar.gz → 1.2.73__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.
Files changed (129) hide show
  1. {fastled-1.2.66 → fastled-1.2.73}/PKG-INFO +1 -2
  2. {fastled-1.2.66 → fastled-1.2.73}/README.md +0 -1
  3. {fastled-1.2.66 → fastled-1.2.73}/src/fastled/__init__.py +4 -2
  4. {fastled-1.2.66 → fastled-1.2.73}/src/fastled/app.py +5 -0
  5. {fastled-1.2.66 → fastled-1.2.73}/src/fastled/compile_server.py +2 -1
  6. {fastled-1.2.66 → fastled-1.2.73}/src/fastled/compile_server_impl.py +43 -22
  7. {fastled-1.2.66 → fastled-1.2.73}/src/fastled/docker_manager.py +117 -20
  8. fastled-1.2.73/src/fastled/keyz.py +84 -0
  9. {fastled-1.2.66 → fastled-1.2.73}/src/fastled/open_browser.py +3 -13
  10. {fastled-1.2.66 → fastled-1.2.73}/src/fastled/parse_args.py +2 -1
  11. {fastled-1.2.66 → fastled-1.2.73}/src/fastled/string_diff.py +22 -0
  12. {fastled-1.2.66 → fastled-1.2.73}/src/fastled/util.py +9 -0
  13. {fastled-1.2.66 → fastled-1.2.73}/src/fastled.egg-info/PKG-INFO +1 -2
  14. fastled-1.2.66/src/fastled/keyz.py +0 -31
  15. {fastled-1.2.66 → fastled-1.2.73}/.aiderignore +0 -0
  16. {fastled-1.2.66 → fastled-1.2.73}/.dockerignore +0 -0
  17. {fastled-1.2.66 → fastled-1.2.73}/.github/workflows/build_multi_docker_image.yml +0 -0
  18. {fastled-1.2.66 → fastled-1.2.73}/.github/workflows/build_webpage.yml +0 -0
  19. {fastled-1.2.66 → fastled-1.2.73}/.github/workflows/lint.yml +0 -0
  20. {fastled-1.2.66 → fastled-1.2.73}/.github/workflows/publish_release.yml +0 -0
  21. {fastled-1.2.66 → fastled-1.2.73}/.github/workflows/template_build_docker_image.yml +0 -0
  22. {fastled-1.2.66 → fastled-1.2.73}/.github/workflows/test_build_exe.yml +0 -0
  23. {fastled-1.2.66 → fastled-1.2.73}/.github/workflows/test_macos.yml +0 -0
  24. {fastled-1.2.66 → fastled-1.2.73}/.github/workflows/test_ubuntu.yml +0 -0
  25. {fastled-1.2.66 → fastled-1.2.73}/.github/workflows/test_win.yml +0 -0
  26. {fastled-1.2.66 → fastled-1.2.73}/.gitignore +0 -0
  27. {fastled-1.2.66 → fastled-1.2.73}/.pylintrc +0 -0
  28. {fastled-1.2.66 → fastled-1.2.73}/.vscode/launch.json +0 -0
  29. {fastled-1.2.66 → fastled-1.2.73}/.vscode/settings.json +0 -0
  30. {fastled-1.2.66 → fastled-1.2.73}/.vscode/tasks.json +0 -0
  31. {fastled-1.2.66 → fastled-1.2.73}/Dockerfile +0 -0
  32. {fastled-1.2.66 → fastled-1.2.73}/LICENSE +0 -0
  33. {fastled-1.2.66 → fastled-1.2.73}/MANIFEST.in +0 -0
  34. {fastled-1.2.66 → fastled-1.2.73}/RELEASE.md +0 -0
  35. {fastled-1.2.66 → fastled-1.2.73}/TODO.md +0 -0
  36. {fastled-1.2.66 → fastled-1.2.73}/build_exe.py +0 -0
  37. {fastled-1.2.66 → fastled-1.2.73}/build_site.py +0 -0
  38. {fastled-1.2.66 → fastled-1.2.73}/clean +0 -0
  39. {fastled-1.2.66 → fastled-1.2.73}/compiler/CMakeLists.txt +0 -0
  40. {fastled-1.2.66 → fastled-1.2.73}/compiler/__init__.py +0 -0
  41. {fastled-1.2.66 → fastled-1.2.73}/compiler/arduino-pre-process.sh +0 -0
  42. {fastled-1.2.66 → fastled-1.2.73}/compiler/build.sh +0 -0
  43. {fastled-1.2.66 → fastled-1.2.73}/compiler/build_archive.sh +0 -0
  44. {fastled-1.2.66 → fastled-1.2.73}/compiler/build_fast.sh +0 -0
  45. {fastled-1.2.66 → fastled-1.2.73}/compiler/code_sync.py +0 -0
  46. {fastled-1.2.66 → fastled-1.2.73}/compiler/compile.py +0 -0
  47. {fastled-1.2.66 → fastled-1.2.73}/compiler/compile_lock.py +0 -0
  48. {fastled-1.2.66 → fastled-1.2.73}/compiler/entrypoint.sh +0 -0
  49. {fastled-1.2.66 → fastled-1.2.73}/compiler/extra/100dots.html +0 -0
  50. {fastled-1.2.66 → fastled-1.2.73}/compiler/extra/demo_threejs.html +0 -0
  51. {fastled-1.2.66 → fastled-1.2.73}/compiler/extra/micdemo.html +0 -0
  52. {fastled-1.2.66 → fastled-1.2.73}/compiler/extra/mp3upload.html +0 -0
  53. {fastled-1.2.66 → fastled-1.2.73}/compiler/extra/webgl_postprocessing_unreal_bloom.html +0 -0
  54. {fastled-1.2.66 → fastled-1.2.73}/compiler/final_prewarm.sh +0 -0
  55. {fastled-1.2.66 → fastled-1.2.73}/compiler/init_runtime.py +0 -0
  56. {fastled-1.2.66 → fastled-1.2.73}/compiler/install-arduino-cli.sh +0 -0
  57. {fastled-1.2.66 → fastled-1.2.73}/compiler/libcompile/CMakeLists.txt +0 -0
  58. {fastled-1.2.66 → fastled-1.2.73}/compiler/paths.py +0 -0
  59. {fastled-1.2.66 → fastled-1.2.73}/compiler/pre-process.sh +0 -0
  60. {fastled-1.2.66 → fastled-1.2.73}/compiler/prewarm.sh +0 -0
  61. {fastled-1.2.66 → fastled-1.2.73}/compiler/process-ino.py +0 -0
  62. {fastled-1.2.66 → fastled-1.2.73}/compiler/process_extended.py +0 -0
  63. {fastled-1.2.66 → fastled-1.2.73}/compiler/pyproject.toml +0 -0
  64. {fastled-1.2.66 → fastled-1.2.73}/compiler/run.py +0 -0
  65. {fastled-1.2.66 → fastled-1.2.73}/compiler/server.py +0 -0
  66. {fastled-1.2.66 → fastled-1.2.73}/compiler/sketch_hasher.py +0 -0
  67. {fastled-1.2.66 → fastled-1.2.73}/compiler/wasm_compiler_flags.py +0 -0
  68. {fastled-1.2.66 → fastled-1.2.73}/docker-compose.yml +0 -0
  69. {fastled-1.2.66 → fastled-1.2.73}/install +0 -0
  70. {fastled-1.2.66 → fastled-1.2.73}/install_linux.sh +0 -0
  71. {fastled-1.2.66 → fastled-1.2.73}/lint +0 -0
  72. {fastled-1.2.66 → fastled-1.2.73}/pyproject.toml +0 -0
  73. {fastled-1.2.66 → fastled-1.2.73}/requirements.testing.txt +0 -0
  74. {fastled-1.2.66 → fastled-1.2.73}/setup.cfg +0 -0
  75. {fastled-1.2.66 → fastled-1.2.73}/setup.py +0 -0
  76. {fastled-1.2.66 → fastled-1.2.73}/src/fastled/assets/example.txt +0 -0
  77. {fastled-1.2.66 → fastled-1.2.73}/src/fastled/assets/localhost-key.pem +0 -0
  78. {fastled-1.2.66 → fastled-1.2.73}/src/fastled/assets/localhost.pem +0 -0
  79. {fastled-1.2.66 → fastled-1.2.73}/src/fastled/cli.py +0 -0
  80. {fastled-1.2.66 → fastled-1.2.73}/src/fastled/cli_test.py +0 -0
  81. {fastled-1.2.66 → fastled-1.2.73}/src/fastled/client_server.py +0 -0
  82. {fastled-1.2.66 → fastled-1.2.73}/src/fastled/filewatcher.py +0 -0
  83. {fastled-1.2.66 → fastled-1.2.73}/src/fastled/interactive_srcs.py +0 -0
  84. {fastled-1.2.66 → fastled-1.2.73}/src/fastled/keyboard.py +0 -0
  85. {fastled-1.2.66 → fastled-1.2.73}/src/fastled/live_client.py +0 -0
  86. {fastled-1.2.66 → fastled-1.2.73}/src/fastled/paths.py +0 -0
  87. {fastled-1.2.66 → fastled-1.2.73}/src/fastled/project_init.py +0 -0
  88. {fastled-1.2.66 → fastled-1.2.73}/src/fastled/select_sketch_directory.py +0 -0
  89. {fastled-1.2.66 → fastled-1.2.73}/src/fastled/server_fastapi.py +0 -0
  90. {fastled-1.2.66 → fastled-1.2.73}/src/fastled/server_fastapi_cli.py +0 -0
  91. {fastled-1.2.66 → fastled-1.2.73}/src/fastled/server_flask.py +0 -0
  92. {fastled-1.2.66 → fastled-1.2.73}/src/fastled/server_start.py +0 -0
  93. {fastled-1.2.66 → fastled-1.2.73}/src/fastled/settings.py +0 -0
  94. {fastled-1.2.66 → fastled-1.2.73}/src/fastled/site/build.py +0 -0
  95. {fastled-1.2.66 → fastled-1.2.73}/src/fastled/site/examples.py +0 -0
  96. {fastled-1.2.66 → fastled-1.2.73}/src/fastled/sketch.py +0 -0
  97. {fastled-1.2.66 → fastled-1.2.73}/src/fastled/spinner.py +0 -0
  98. {fastled-1.2.66 → fastled-1.2.73}/src/fastled/test/can_run_local_docker_tests.py +0 -0
  99. {fastled-1.2.66 → fastled-1.2.73}/src/fastled/test/examples.py +0 -0
  100. {fastled-1.2.66 → fastled-1.2.73}/src/fastled/types.py +0 -0
  101. {fastled-1.2.66 → fastled-1.2.73}/src/fastled/web_compile.py +0 -0
  102. {fastled-1.2.66 → fastled-1.2.73}/src/fastled.egg-info/SOURCES.txt +0 -0
  103. {fastled-1.2.66 → fastled-1.2.73}/src/fastled.egg-info/dependency_links.txt +0 -0
  104. {fastled-1.2.66 → fastled-1.2.73}/src/fastled.egg-info/entry_points.txt +0 -0
  105. {fastled-1.2.66 → fastled-1.2.73}/src/fastled.egg-info/requires.txt +0 -0
  106. {fastled-1.2.66 → fastled-1.2.73}/src/fastled.egg-info/top_level.txt +0 -0
  107. {fastled-1.2.66 → fastled-1.2.73}/test +0 -0
  108. {fastled-1.2.66 → fastled-1.2.73}/tests/integration/test_build_examples.py +0 -0
  109. {fastled-1.2.66 → fastled-1.2.73}/tests/integration/test_examples.py +0 -0
  110. {fastled-1.2.66 → fastled-1.2.73}/tests/unit/html/index.html +0 -0
  111. {fastled-1.2.66 → fastled-1.2.73}/tests/unit/test_api.py +0 -0
  112. {fastled-1.2.66 → fastled-1.2.73}/tests/unit/test_bad_ino.py +0 -0
  113. {fastled-1.2.66 → fastled-1.2.73}/tests/unit/test_cli.py +0 -0
  114. {fastled-1.2.66 → fastled-1.2.73}/tests/unit/test_compile_server.py +0 -0
  115. {fastled-1.2.66 → fastled-1.2.73}/tests/unit/test_docker_linux_on_windows.py +0 -0
  116. {fastled-1.2.66 → fastled-1.2.73}/tests/unit/test_embedded_data.py +0 -0
  117. {fastled-1.2.66 → fastled-1.2.73}/tests/unit/test_filechanger.py +0 -0
  118. {fastled-1.2.66 → fastled-1.2.73}/tests/unit/test_http_server.py +0 -0
  119. {fastled-1.2.66 → fastled-1.2.73}/tests/unit/test_ino/bad/bad.ino +0 -0
  120. {fastled-1.2.66 → fastled-1.2.73}/tests/unit/test_ino/bad_platformio/bad_platformio.ino +0 -0
  121. {fastled-1.2.66 → fastled-1.2.73}/tests/unit/test_ino/bad_platformio/platformio.ini +0 -0
  122. {fastled-1.2.66 → fastled-1.2.73}/tests/unit/test_ino/embedded/data/bigdata.dat +0 -0
  123. {fastled-1.2.66 → fastled-1.2.73}/tests/unit/test_ino/embedded/wasm.ino +0 -0
  124. {fastled-1.2.66 → fastled-1.2.73}/tests/unit/test_ino/wasm/wasm.ino +0 -0
  125. {fastled-1.2.66 → fastled-1.2.73}/tests/unit/test_project_init.py +0 -0
  126. {fastled-1.2.66 → fastled-1.2.73}/tests/unit/test_server_and_client_seperatly.py +0 -0
  127. {fastled-1.2.66 → fastled-1.2.73}/tests/unit/test_string_diff.py +0 -0
  128. {fastled-1.2.66 → fastled-1.2.73}/tests/unit/test_webcompile.py +0 -0
  129. {fastled-1.2.66 → fastled-1.2.73}/upload_package.sh +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fastled
3
- Version: 1.2.66
3
+ Version: 1.2.73
4
4
  Summary: FastLED Wasm Compiler
5
5
  Home-page: https://github.com/zackees/fastled-wasm
6
6
  Maintainer: Zachary Vorhies
@@ -300,7 +300,6 @@ Q: How can I get the compiled size of my FastLED sketch smaller?
300
300
  A: A big chunk of space is being used by unnecessary javascript `emscripten` bundling. The wasm_compiler_settings.py file in the FastLED repo can tweak this.
301
301
 
302
302
 
303
-
304
303
  # Revisions
305
304
  * 1.2.31 - Bunch of fixes and ease of use while compiling code in the repo.
306
305
  * 1.2.22 - Prefer to use `live-server` from npm. If npm exists on the system then do a background install of `live-server` for next run.
@@ -269,7 +269,6 @@ Q: How can I get the compiled size of my FastLED sketch smaller?
269
269
  A: A big chunk of space is being used by unnecessary javascript `emscripten` bundling. The wasm_compiler_settings.py file in the FastLED repo can tweak this.
270
270
 
271
271
 
272
-
273
272
  # Revisions
274
273
  * 1.2.31 - Bunch of fixes and ease of use while compiling code in the repo.
275
274
  * 1.2.22 - Prefer to use `live-server` from npm. If npm exists on the system then do a background install of `live-server` for next run.
@@ -13,7 +13,7 @@ from .types import BuildMode, CompileResult, CompileServerError
13
13
  # IMPORTANT! There's a bug in github which will REJECT any version update
14
14
  # that has any other change in the repo. Please bump the version as the
15
15
  # ONLY change in a commit, or else the pypi update and the release will fail.
16
- __version__ = "1.2.66"
16
+ __version__ = "1.2.73"
17
17
 
18
18
  DOCKER_FILE = (
19
19
  "https://raw.githubusercontent.com/zackees/fastled-wasm/refs/heads/main/Dockerfile"
@@ -137,7 +137,9 @@ class Docker:
137
137
  def is_running() -> bool:
138
138
  from fastled.docker_manager import DockerManager
139
139
 
140
- return DockerManager.is_running()
140
+ ok: bool
141
+ ok, _ = DockerManager.is_running()
142
+ return ok
141
143
 
142
144
  @staticmethod
143
145
  def is_container_running(container_name: str | None = None) -> bool:
@@ -43,6 +43,8 @@ def run_server(args: Args) -> int:
43
43
 
44
44
 
45
45
  def main() -> int:
46
+ from fastled import __version__
47
+
46
48
  args = parse_args()
47
49
  interactive: bool = args.interactive
48
50
  has_server = args.server
@@ -52,6 +54,9 @@ def main() -> int:
52
54
  # directory: Path | None = Path(args.directory).absolute() if args.directory else None
53
55
  directory: Path | None = Path(args.directory) if args.directory else None
54
56
 
57
+ # now it is safe to print out the version
58
+ print(f"FastLED version: {__version__}")
59
+
55
60
  if update:
56
61
  # Force auto_update to ensure update check happens
57
62
  compile_server = CompileServer(interactive=False, auto_updates=True)
@@ -59,7 +59,8 @@ class CompileServer:
59
59
 
60
60
  @property
61
61
  def running(self) -> bool:
62
- return self.impl.running
62
+ ok, _ = self.impl.running
63
+ return ok
63
64
 
64
65
  @property
65
66
  def fastled_src_dir(self) -> Path | None:
@@ -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,6 +13,7 @@ from fastled.docker_manager import (
12
13
  Container,
13
14
  DockerManager,
14
15
  RunningContainer,
16
+ Volume,
15
17
  )
16
18
  from fastled.interactive_srcs import INTERACTIVE_SOURCES
17
19
  from fastled.settings import DEFAULT_CONTAINER_NAME, IMAGE_NAME, SERVER_PORT
@@ -111,14 +113,24 @@ class CompileServerImpl:
111
113
  project_init(example=example, outputdir=outputdir)
112
114
 
113
115
  @property
114
- def running(self) -> bool:
116
+ def running(self) -> tuple[bool, Exception | None]:
115
117
  if not self._port:
116
- return False
118
+ return False, Exception("Docker hasn't been initialzed with a port yet")
117
119
  if not DockerManager.is_docker_installed():
118
- return False
119
- if not DockerManager.is_running():
120
- return False
121
- return self.docker.is_container_running(self.container_name)
120
+ return False, Exception("Docker is not installed")
121
+ docker_running, err = self.docker.is_running()
122
+ if not docker_running:
123
+ IS_MAC = sys.platform == "darwin"
124
+ if IS_MAC:
125
+ if "FileNotFoundError" in str(err):
126
+ traceback.print_exc()
127
+ print("\n\nNone fatal debug print for MacOS\n")
128
+ return False, err
129
+ ok: bool = self.docker.is_container_running(self.container_name)
130
+ if ok:
131
+ return True, None
132
+ else:
133
+ return False, Exception("Docker is not running")
122
134
 
123
135
  def using_fastled_src_dir_volume(self) -> bool:
124
136
  out = self.fastled_src_dir is not None
@@ -198,27 +210,32 @@ class CompileServerImpl:
198
210
  else:
199
211
  server_command = ["python", "/js/run.py", "server"] + SERVER_OPTIONS
200
212
  ports = {80: port}
201
- volumes = None
213
+ volumes = []
202
214
  if self.fastled_src_dir:
203
215
  print(
204
216
  f"Mounting FastLED source directory {self.fastled_src_dir} into container /host/fastled/src"
205
217
  )
206
- volumes = {
207
- str(self.fastled_src_dir): {"bind": "/host/fastled/src", "mode": "ro"}
208
- }
218
+ volumes.append(
219
+ Volume(
220
+ host_path=str(self.fastled_src_dir),
221
+ container_path="/host/fastled/src",
222
+ mode="ro",
223
+ )
224
+ )
209
225
  if self.interactive:
210
226
  # add the mapped directory to the container
211
227
  print(f"Mounting {self.mapped_dir} into container /mapped")
212
- # volumes = {str(self.mapped_dir): {"bind": "/mapped", "mode": "rw"}}
213
- # add it
214
228
  assert self.mapped_dir is not None
215
229
  dir_name = self.mapped_dir.name
216
230
  if not volumes:
217
- volumes = {}
218
- volumes[str(self.mapped_dir)] = {
219
- "bind": f"/mapped/{dir_name}",
220
- "mode": "rw",
221
- }
231
+ volumes = []
232
+ volumes.append(
233
+ Volume(
234
+ host_path=str(self.mapped_dir),
235
+ container_path=f"/mapped/{dir_name}",
236
+ mode="rw",
237
+ )
238
+ )
222
239
  if self.fastled_src_dir is not None:
223
240
  # to allow for interactive compilation
224
241
  interactive_sources = list(INTERACTIVE_SOURCES)
@@ -226,11 +243,13 @@ class CompileServerImpl:
226
243
  src_path = Path(src).absolute()
227
244
  if src_path.exists():
228
245
  print(f"Mounting {src} into container")
229
- src_str = str(src_path)
230
- volumes[src_str] = {
231
- "bind": f"/js/fastled/{src}",
232
- "mode": "rw",
233
- }
246
+ volumes.append(
247
+ Volume(
248
+ host_path=str(src_path),
249
+ container_path=f"/js/fastled/{src}",
250
+ mode="rw",
251
+ )
252
+ )
234
253
  else:
235
254
  print(f"Could not find {src}")
236
255
 
@@ -266,6 +285,8 @@ class CompileServerImpl:
266
285
  return self.docker.is_container_running(self.container_name)
267
286
 
268
287
  def stop(self) -> None:
288
+ if self.docker.is_suspended:
289
+ return
269
290
  if self.running_container:
270
291
  self.running_container.detach()
271
292
  self.running_container = None
@@ -3,6 +3,7 @@ New abstraction for Docker management with improved Ctrl+C handling.
3
3
  """
4
4
 
5
5
  import _thread
6
+ import json
6
7
  import os
7
8
  import platform
8
9
  import subprocess
@@ -11,6 +12,7 @@ import threading
11
12
  import time
12
13
  import traceback
13
14
  import warnings
15
+ from dataclasses import dataclass
14
16
  from datetime import datetime, timezone
15
17
  from pathlib import Path
16
18
 
@@ -63,6 +65,40 @@ def get_lock(image_name: str) -> FileLock:
63
65
  return out
64
66
 
65
67
 
68
+ @dataclass
69
+ class Volume:
70
+ """
71
+ Represents a Docker volume mapping between host and container.
72
+
73
+ Attributes:
74
+ host_path: Path on the host system (e.g., "C:\\Users\\username\\project")
75
+ container_path: Path inside the container (e.g., "/app/data")
76
+ mode: Access mode, "rw" for read-write or "ro" for read-only
77
+ """
78
+
79
+ host_path: str
80
+ container_path: str
81
+ mode: str = "rw"
82
+
83
+ def to_dict(self) -> dict[str, dict[str, str]]:
84
+ """Convert the Volume object to the format expected by Docker API."""
85
+ return {self.host_path: {"bind": self.container_path, "mode": self.mode}}
86
+
87
+ @classmethod
88
+ def from_dict(cls, volume_dict: dict[str, dict[str, str]]) -> list["Volume"]:
89
+ """Create Volume objects from a Docker volume dictionary."""
90
+ volumes = []
91
+ for host_path, config in volume_dict.items():
92
+ volumes.append(
93
+ cls(
94
+ host_path=host_path,
95
+ container_path=config["bind"],
96
+ mode=config.get("mode", "rw"),
97
+ )
98
+ )
99
+ return volumes
100
+
101
+
66
102
  class RunningContainer:
67
103
  def __init__(self, container, first_run=False):
68
104
  self.container = container
@@ -104,10 +140,46 @@ class RunningContainer:
104
140
  self.detach()
105
141
 
106
142
 
143
+ def _hack_to_fix_mac(volumes: list[Volume] | None) -> list[Volume] | None:
144
+ """Fixes the volume mounts on MacOS by removing the mode."""
145
+ if volumes is None:
146
+ return None
147
+ if sys.platform != "darwin":
148
+ # Only macos needs hacking.
149
+ return volumes
150
+
151
+ volumes = volumes.copy()
152
+ # Work around a Docker bug on MacOS where the expected network socket to the
153
+ # the host is not mounted correctly. This was actually fixed in recent versions
154
+ # of docker client but there is a large chunk of Docker clients out there with
155
+ # this bug in it.
156
+ #
157
+ # This hack is done by mounting the socket directly to the container.
158
+ # This socket talks to the docker daemon on the host.
159
+ #
160
+ # Found here.
161
+ # https://github.com/docker/docker-py/issues/3069#issuecomment-1316778735
162
+ # if it exists already then return the input
163
+ for volume in volumes:
164
+ if volume.host_path == "/var/run/docker.sock":
165
+ return volumes
166
+ # ok it doesn't exist, so add it
167
+ volumes.append(
168
+ Volume(
169
+ host_path="/var/run/docker.sock",
170
+ container_path="/var/run/docker.sock",
171
+ mode="rw",
172
+ )
173
+ )
174
+ return volumes
175
+
176
+
107
177
  class DockerManager:
108
178
  def __init__(self) -> None:
109
179
  from docker.errors import DockerException
110
180
 
181
+ self.is_suspended: bool = False
182
+
111
183
  try:
112
184
  self._client: DockerClient | None = None
113
185
  self.first_run = False
@@ -201,24 +273,24 @@ class DockerManager:
201
273
  return False
202
274
 
203
275
  @staticmethod
204
- def is_running() -> bool:
276
+ def is_running() -> tuple[bool, Exception | None]:
205
277
  """Check if Docker is running by pinging the Docker daemon."""
206
278
 
207
279
  if not DockerManager.is_docker_installed():
208
280
  print("Docker is not installed.")
209
- return False
281
+ return False, Exception("Docker is not installed.")
210
282
  try:
211
283
  # self.client.ping()
212
284
  client = docker.from_env()
213
285
  client.ping()
214
286
  print("Docker is running.")
215
- return True
287
+ return True, None
216
288
  except DockerException as e:
217
289
  print(f"Docker is not running: {str(e)}")
218
- return False
290
+ return False, e
219
291
  except Exception as e:
220
292
  print(f"Error pinging Docker daemon: {str(e)}")
221
- return False
293
+ return False, e
222
294
 
223
295
  def start(self) -> bool:
224
296
  """Attempt to start Docker Desktop (or the Docker daemon) automatically."""
@@ -314,8 +386,6 @@ class DockerManager:
314
386
 
315
387
  # Quick check for latest version
316
388
  with Spinner(f"Pulling newer version of {image_name}:{tag}..."):
317
- # This needs to be swapped out using the the command line interface AI!
318
- # _ = self.client.images.pull(image_name, tag=tag)
319
389
  cmd_list = ["docker", "pull", f"{image_name}:{tag}"]
320
390
  cmd_str = subprocess.list2cmdline(cmd_list)
321
391
  print(f"Running command: {cmd_str}")
@@ -355,8 +425,8 @@ class DockerManager:
355
425
  self,
356
426
  container: Container,
357
427
  command: str | None,
358
- volumes: dict | None,
359
- ports: dict | None,
428
+ volumes_dict: dict[str, dict[str, str]] | None,
429
+ ports: dict[int, int] | None,
360
430
  ) -> bool:
361
431
  """Compare if existing container has matching configuration"""
362
432
  try:
@@ -386,7 +456,7 @@ class DockerManager:
386
456
  return False
387
457
 
388
458
  # Check volumes if specified
389
- if volumes:
459
+ if volumes_dict:
390
460
  container_mounts = (
391
461
  {
392
462
  m["Source"]: {"bind": m["Destination"], "mode": m["Mode"]}
@@ -396,7 +466,7 @@ class DockerManager:
396
466
  else {}
397
467
  )
398
468
 
399
- for host_dir, mount in volumes.items():
469
+ for host_dir, mount in volumes_dict.items():
400
470
  if host_dir not in container_mounts:
401
471
  print(f"Volume {host_dir} not found in container mounts.")
402
472
  return False
@@ -446,7 +516,7 @@ class DockerManager:
446
516
  tag: str,
447
517
  container_name: str,
448
518
  command: str | None = None,
449
- volumes: dict[str, dict[str, str]] | None = None,
519
+ volumes: list[Volume] | None = None,
450
520
  ports: dict[int, int] | None = None,
451
521
  remove_previous: bool = False,
452
522
  ) -> Container:
@@ -455,11 +525,23 @@ class DockerManager:
455
525
  If it exists with different config, remove and recreate it.
456
526
 
457
527
  Args:
458
- volumes: Dict mapping host paths to dicts with 'bind' and 'mode' keys
459
- Example: {'/host/path': {'bind': '/container/path', 'mode': 'rw'}}
528
+ volumes: List of Volume objects for container volume mappings
460
529
  ports: Dict mapping host ports to container ports
461
530
  Example: {8080: 80} maps host port 8080 to container port 80
462
531
  """
532
+ volumes = _hack_to_fix_mac(volumes)
533
+ # Convert volumes to the format expected by Docker API
534
+ volumes_dict = None
535
+ if volumes is not None:
536
+ volumes_dict = {}
537
+ for volume in volumes:
538
+ volumes_dict.update(volume.to_dict())
539
+
540
+ # Serialize the volumes to a json string
541
+ if volumes_dict:
542
+ volumes_str = json.dumps(volumes_dict)
543
+ print(f"Volumes: {volumes_str}")
544
+ print("Done")
463
545
  image_name = f"{image_name}:{tag}"
464
546
  try:
465
547
  container: Container = self.client.containers.get(container_name)
@@ -469,7 +551,9 @@ class DockerManager:
469
551
  container.remove(force=True)
470
552
  raise NotFound("Container removed due to remove_previous")
471
553
  # Check if configuration matches
472
- elif not self._container_configs_match(container, command, volumes, ports):
554
+ elif not self._container_configs_match(
555
+ container, command, volumes_dict, ports
556
+ ):
473
557
  print(
474
558
  f"Container {container_name} exists but with different configuration. Removing and recreating..."
475
559
  )
@@ -517,7 +601,7 @@ class DockerManager:
517
601
  name=container_name,
518
602
  detach=True,
519
603
  tty=True,
520
- volumes=volumes,
604
+ volumes=volumes_dict,
521
605
  ports=ports, # type: ignore
522
606
  remove=True,
523
607
  )
@@ -529,9 +613,16 @@ class DockerManager:
529
613
  tag: str,
530
614
  container_name: str,
531
615
  command: str | None = None,
532
- volumes: dict[str, dict[str, str]] | None = None,
616
+ volumes: list[Volume] | None = None,
533
617
  ports: dict[int, int] | None = None,
534
618
  ) -> None:
619
+ # Convert volumes to the format expected by Docker API
620
+ volumes = _hack_to_fix_mac(volumes)
621
+ volumes_dict = None
622
+ if volumes is not None:
623
+ volumes_dict = {}
624
+ for volume in volumes:
625
+ volumes_dict.update(volume.to_dict())
535
626
  # Remove existing container
536
627
  try:
537
628
  container: Container = self.client.containers.get(container_name)
@@ -548,9 +639,13 @@ class DockerManager:
548
639
  "--name",
549
640
  container_name,
550
641
  ]
551
- if volumes:
552
- for host_dir, mount in volumes.items():
553
- docker_command.extend(["-v", f"{host_dir}:{mount['bind']}"])
642
+ if volumes_dict:
643
+ for host_dir, mount in volumes_dict.items():
644
+ docker_volume_arg = [
645
+ "-v",
646
+ f"{host_dir}:{mount['bind']}:{mount['mode']}",
647
+ ]
648
+ docker_command.extend(docker_volume_arg)
554
649
  if ports:
555
650
  for host_port, container_port in ports.items():
556
651
  docker_command.extend(["-p", f"{host_port}:{container_port}"])
@@ -591,6 +686,8 @@ class DockerManager:
591
686
  """
592
687
  Suspend (pause) the container.
593
688
  """
689
+ if self.is_suspended:
690
+ return
594
691
  if isinstance(container, str):
595
692
  container_name = container
596
693
  # container = self.get_container(container)
@@ -0,0 +1,84 @@
1
+ # This was an experiment for https://localhost
2
+ # Important for audio sampling from sites like Youtube or an app like Audacity.
3
+ # However, pkg_resources does not work in the exe version of this app.
4
+ # For this to work pyinstaller needs special handling (probably a spec-file)
5
+ # in order to properly package up external resources.
6
+
7
+
8
+ from dataclasses import dataclass
9
+
10
+ _ENABLE_SSL_CONFIG = False
11
+
12
+
13
+ @dataclass
14
+ class SslConfig:
15
+ cert: str
16
+ key: str
17
+
18
+
19
+ def get_ssl_config() -> SslConfig | None:
20
+ """Get the keys for the server"""
21
+ if not _ENABLE_SSL_CONFIG:
22
+ return None
23
+ return SslConfig(
24
+ cert=_CERT,
25
+ key=_PRIVATE_KEY,
26
+ )
27
+
28
+
29
+ _PRIVATE_KEY = """-----BEGIN PRIVATE KEY-----
30
+ MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDlxbWcpUXPpjqs
31
+ DJPgFF1FsXPZqq0JPJqssHh4ZLfN0h4yJmj+kRcHS+pkgXnG46g6bUcL/AK5Ba08
32
+ vwnUUGkPH0v4ShKiAGYwvOcbWaqTmvvJuIoaDBXh2jSCeOTagNoaHLYEugARkkEu
33
+ 0/FEW5P/79wU5vJ5G+SyZ8rBCVdxlU57pL1hKWBU7K+BLWsCiZ308NMpzHF5APZ6
34
+ YxVjhFosJPr4TjN6yXr+whrsAjSTHamD5690MbXWyyPG0jwPQyjBot/cNtt8GrsN
35
+ gcjA1E+8VKFvxO8RvZanMZLb0CGEpt7u3oaJ/jprHEsw+/UhnG6Qhksm8C/DN9kP
36
+ hselewffAgMBAAECggEARjQ6YTo+Mkvf8WGGbRjLxteJRiBX7lKOD+V7aY2ce06P
37
+ 21LREbbTCm+vljXZN2OnqvJomsjNLCsH21+jaTOIZg5x79LyDn2Au7N8CWdELwVT
38
+ mTbBO2Ql63P4R0UY54onGYNcOeV6z+OX9u7a8L/qYHCxFdHalBZpsfj0gjaQeStJ
39
+ JSnvGjo6tKkwC/nUmX01qEVQgUO1+39WYqCaIWjijZNXt6XiKclEuu1AkL0u6Mpt
40
+ CzvzEDrEA66D0Lvl3Tek9B4O16Oie5anNnNMHigwU9yVc6dI8vDCRSEiz7laPTFK
41
+ xzOCQmqPGClKXkX3U+OvZp/Ss9U26Wpu0AbRKTvzAQKBgQDsMR9NjMpOmUaWkAwl
42
+ 1wlUxsZ9YkRuTy7R3RfIdYWj6Lcoc4/iN0qILFM7xidkHhYTFqnsnP1SQkV6lEHV
43
+ OalYxZu9F2l1rHPc8G5YWh/KOg1rAEI47MVT4iwhA2fw6JLti/rm25AeSTMjSTqu
44
+ ht3146036opcIF3v86oGUrSXDwKBgQD5CsNcwLeUDGXozjq62T8/mTYwd2Tw3aiY
45
+ KaGp+exAW321vYm5SKsMitBMGU2tGFlv5eptSI48h7SCpgnszaexw7yj30KuvqjG
46
+ bBqq/MsKuXHyn2sG0A7MJ6zfk+4l46B45blDJZ+x7xL0dyS4UCU3zUeesgSGo4zK
47
+ ZOspPIQCMQKBgQCk35VuWP1P6IbxyxPvxi/pUeh01gfWyMdyD9fuQrtLM8PHJQQn
48
+ cVlBvU9MxoHwzV+za3qqhNwAc+p0KtHZuiqQoUCZuqIPVpZ6gAtG+YJ/dA6xxrhz
49
+ bDRC3frYALyp2m/WCoTWaiYsPgTIePHRqqt+XbQo+DwlGyL3wSvKxijx2QKBgCb0
50
+ OwioEE70/X/DukX9szn0chh0pHJUiYl7gZD/yadraCdkRUWZC0BD+j7c+lxn4Z1y
51
+ HhAH+E+Zfm+tHwJOTLuufTQ4uMpygh2/TRCPyAaeaSdlLi17n8TpM84o6mg8yZ3/
52
+ eNH68Za4aYOZm0HFL30h++DjwXd534zM6keh8pgRAoGBAKUrsjDGjuSo8l1fi4Cq
53
+ INu/rQop2h/db02zyJP5q7NKhE1nqogeLwwn+2M/LtHQ1nIzZR+rvrLNgt6oWY31
54
+ sPsv8JUfVT8GmdhU9KKmizK6eUu3rWdj2+rJARmuEaPmHcD5O6oJaGU0qadqQP34
55
+ H+enwWmpyZXAIbEu/q63DFhV
56
+ -----END PRIVATE KEY-----"""
57
+
58
+ _CERT = """-----BEGIN _CERTIFICATE-----
59
+ MIIEfTCCAuWgAwIBAgIRAPb7jkLrCuqToG+s3AQYeuUwDQYJKoZIhvcNAQELBQAw
60
+ gakxHjAcBgNVBAoTFW1rY2VydCBkZXZlbG9wbWVudCBDQTE/MD0GA1UECww2REVT
61
+ S1RPUC1JMzcxOERPXFphY2ggVm9yaGllc0BERVNLVE9QLUkzNzE4RE8gKG5pdGVy
62
+ aXMpMUYwRAYDVQQDDD1ta2NlcnQgREVTS1RPUC1JMzcxOERPXFphY2ggVm9yaGll
63
+ c0BERVNLVE9QLUkzNzE4RE8gKG5pdGVyaXMpMB4XDTI1MDQyODAwMzk1MFoXDTI3
64
+ MDcyODAwMzk1MFowajEnMCUGA1UEChMebWtjZXJ0IGRldmVsb3BtZW50IGNlcnRp
65
+ ZmljYXRlMT8wPQYDVQQLDDZERVNLVE9QLUkzNzE4RE9cWmFjaCBWb3JoaWVzQERF
66
+ U0tUT1AtSTM3MThETyAobml0ZXJpcykwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
67
+ ggEKAoIBAQDlxbWcpUXPpjqsDJPgFF1FsXPZqq0JPJqssHh4ZLfN0h4yJmj+kRcH
68
+ S+pkgXnG46g6bUcL/AK5Ba08vwnUUGkPH0v4ShKiAGYwvOcbWaqTmvvJuIoaDBXh
69
+ 2jSCeOTagNoaHLYEugARkkEu0/FEW5P/79wU5vJ5G+SyZ8rBCVdxlU57pL1hKWBU
70
+ 7K+BLWsCiZ308NMpzHF5APZ6YxVjhFosJPr4TjN6yXr+whrsAjSTHamD5690MbXW
71
+ yyPG0jwPQyjBot/cNtt8GrsNgcjA1E+8VKFvxO8RvZanMZLb0CGEpt7u3oaJ/jpr
72
+ HEsw+/UhnG6Qhksm8C/DN9kPhselewffAgMBAAGjXjBcMA4GA1UdDwEB/wQEAwIF
73
+ oDATBgNVHSUEDDAKBggrBgEFBQcDATAfBgNVHSMEGDAWgBSPBydvhr9wI+FsoW/H
74
+ WK3DbS8IUDAUBgNVHREEDTALgglsb2NhbGhvc3QwDQYJKoZIhvcNAQELBQADggGB
75
+ AJVrF1yczZaxt+A2AhdeFbJQUR6NzGBTc20YeWF1YzLV5sV3QVumwZLP2M9ggRgd
76
+ xWV0xfwUHobFQk6RIPTADcLKctiurql0cgF4DPnpWVvto9RM00U3AkQcMj3xtKBV
77
+ wUqo83TcbqgL+euudFZ09gGTs9u9AENaZPcMh+rW8DDO92t+EwMI/IfopxVOJGUB
78
+ RSM3yTwV93BMYBuddt8mclzLzPK/1WONfsHU2xEascaHR1tYMOmJN9Vq4o0fzWxo
79
+ a2vI6K0aJqZV/ztdXq3akwLc6/e9hyptHWa0i/022xVCyNWIlnuEhT7ENMPxh6rX
80
+ ZCQCZVnhcSWAyFjggLJql3aSID5fPF8rmN7wWsB/I5pl9qwMR1/THMPrm5aWn1Xj
81
+ xW6PxkSGm73kd57DH7tqm5HTd8eYCbnsFofI9rC7xI6HCfwchKp+YHvIEu/LJ56E
82
+ FLnCZW/orYkHCzWntzxv1bddrw1BwaNR8Q+mu3imRP8fuyXb2UkFkINVVyOOWHuW
83
+ Kw==
84
+ -----END _CERTIFICATE-----"""
@@ -8,7 +8,6 @@ from fastled.keyz import get_ssl_config
8
8
 
9
9
  DEFAULT_PORT = 8089 # different than live version.
10
10
  PYTHON_EXE = sys.executable
11
- SSL_CONFIG = get_ssl_config()
12
11
 
13
12
 
14
13
  # print(f"SSL Config: {SSL_CONFIG.certfile}, {SSL_CONFIG.keyfile}")
@@ -21,6 +20,7 @@ def _open_http_server_subprocess(
21
20
  print("\n################################################################")
22
21
  print(f"# Opening browser to {fastled_js} on port {port}")
23
22
  print("################################################################\n")
23
+ ssl = get_ssl_config()
24
24
  try:
25
25
  # Fallback to our Python server
26
26
  cmd = [
@@ -32,18 +32,8 @@ def _open_http_server_subprocess(
32
32
  str(port),
33
33
  ]
34
34
  # Pass SSL flags if available
35
- if SSL_CONFIG and SSL_CONFIG.certfile and SSL_CONFIG.keyfile:
36
- cmd.extend(
37
- [
38
- "--cert",
39
- str(SSL_CONFIG.certfile),
40
- "--key",
41
- str(SSL_CONFIG.keyfile),
42
- ]
43
- )
44
- print(
45
- f"Running server on port {port} with certs: {SSL_CONFIG.certfile}, {SSL_CONFIG.keyfile}"
46
- )
35
+ if ssl:
36
+ raise NotImplementedError("SSL is not implemented yet")
47
37
  print(f"Running server on port {port}.")
48
38
  print(f"Command: {subprocess.list2cmdline(cmd)}")
49
39
  # Suppress output
@@ -3,7 +3,6 @@ import os
3
3
  import sys
4
4
  from pathlib import Path
5
5
 
6
- from fastled import __version__
7
6
  from fastled.project_init import project_init
8
7
  from fastled.select_sketch_directory import select_sketch_directory
9
8
  from fastled.settings import DEFAULT_URL, IMAGE_NAME
@@ -27,6 +26,8 @@ def _find_fastled_repo(start: Path) -> Path | None:
27
26
 
28
27
  def parse_args() -> Args:
29
28
  """Parse command-line arguments."""
29
+ from fastled import __version__
30
+
30
31
  parser = argparse.ArgumentParser(description=f"FastLED WASM Compiler {__version__}")
31
32
  parser.add_argument("--version", action="version", version=f"{__version__}")
32
33
  parser.add_argument(
@@ -3,6 +3,24 @@ from pathlib import Path
3
3
  from rapidfuzz import fuzz
4
4
 
5
5
 
6
+ def _filter_out_obvious_bad_choices(
7
+ input_str: str, string_list: list[str]
8
+ ) -> list[str]:
9
+ """
10
+ Filter out strings that are too different from the input string.
11
+ This is a heuristic and may not be perfect.
12
+ """
13
+ input_chars = set(input_str)
14
+ filtered_list = []
15
+ for s in string_list:
16
+ # Check if at least half of the input characters are in the string
17
+ s_chars = set(s)
18
+ common_chars = input_chars.intersection(s_chars)
19
+ if len(common_chars) >= len(input_chars) / 2:
20
+ filtered_list.append(s)
21
+ return filtered_list
22
+
23
+
6
24
  # Returns the min distance strings. If there is a tie, it returns
7
25
  # all the strings that have the same min distance.
8
26
  # Returns a tuple of index and string.
@@ -24,6 +42,10 @@ def string_diff(
24
42
  string_list = [s.lower() for s in string_list]
25
43
  input_string = input_string.lower()
26
44
 
45
+ # Apply set membership filtering for queries with 3+ characters
46
+ if len(input_string) >= 3:
47
+ string_list = _filter_out_obvious_bad_choices(input_string, string_list)
48
+
27
49
  is_substring = False
28
50
  for s in string_list:
29
51
  if input_string in s:
@@ -8,3 +8,12 @@ def hash_file(file_path: Path) -> str:
8
8
  for chunk in iter(lambda: f.read(4096), b""):
9
9
  hasher.update(chunk)
10
10
  return hasher.hexdigest()
11
+
12
+
13
+ def banner_string(msg: str) -> str:
14
+ """
15
+ Return `msg` surrounded by a border of # characters, including
16
+ leading and trailing newlines.
17
+ """
18
+ border = "#" * (len(msg) + 4)
19
+ return f"\n{border}\n# {msg}\n{border}\n"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fastled
3
- Version: 1.2.66
3
+ Version: 1.2.73
4
4
  Summary: FastLED Wasm Compiler
5
5
  Home-page: https://github.com/zackees/fastled-wasm
6
6
  Maintainer: Zachary Vorhies
@@ -300,7 +300,6 @@ Q: How can I get the compiled size of my FastLED sketch smaller?
300
300
  A: A big chunk of space is being used by unnecessary javascript `emscripten` bundling. The wasm_compiler_settings.py file in the FastLED repo can tweak this.
301
301
 
302
302
 
303
-
304
303
  # Revisions
305
304
  * 1.2.31 - Bunch of fixes and ease of use while compiling code in the repo.
306
305
  * 1.2.22 - Prefer to use `live-server` from npm. If npm exists on the system then do a background install of `live-server` for next run.
@@ -1,31 +0,0 @@
1
- import importlib.resources as pkg_resources
2
- from dataclasses import dataclass
3
- from pathlib import Path
4
-
5
-
6
- @dataclass
7
- class SslConfig:
8
- certfile: Path
9
- keyfile: Path
10
-
11
-
12
- def get_asset_path(filename: str) -> Path | None:
13
- """Locate a file from the fastled.assets package resources."""
14
- try:
15
- resource = pkg_resources.files("fastled.assets").joinpath(filename)
16
- # Convert to Path for file-system access
17
- path = Path(str(resource))
18
- return path if path.exists() else None
19
- except (ModuleNotFoundError, AttributeError):
20
- return None
21
-
22
-
23
- def get_ssl_config() -> SslConfig | None:
24
- """Get the keys for the server"""
25
- # certfile = get_asset_path("localhost-key.pem")
26
- # keyfile = get_asset_path("localhost.pem")
27
- # if certfile is None or keyfile is None:
28
- # raise ValueError("Could not find keys for server")
29
- # # return certfile, keyfile
30
- # return SslConfig(certfile=certfile, keyfile=keyfile)
31
- return None
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes