fastled 1.1.6__py2.py3-none-any.whl → 1.1.15__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
@@ -0,0 +1,3 @@
1
+ """FastLED Wasm Compiler package."""
2
+
3
+ __version__ = "1.1.15"
fastled/app.py CHANGED
@@ -1,7 +1,5 @@
1
1
  """
2
2
  Uses the latest wasm compiler image to compile the FastLED sketch.
3
-
4
-
5
3
  """
6
4
 
7
5
  import argparse
@@ -15,13 +13,20 @@ import time
15
13
  from dataclasses import dataclass
16
14
  from pathlib import Path
17
15
 
16
+ from fastled import __version__
18
17
  from fastled.build_mode import BuildMode, get_build_mode
19
- from fastled.compile_server import CompileServer, looks_like_fastled_repo
18
+ from fastled.compile_server import CompileServer
20
19
  from fastled.docker_manager import DockerManager
21
- from fastled.filewatcher import FileChangedNotifier
20
+ from fastled.filewatcher import FileWatcherProcess
21
+ from fastled.keyboard import SpaceBarWatcher
22
22
  from fastled.open_browser import open_browser_thread
23
- from fastled.sketch import get_sketch_files
24
- from fastled.web_compile import web_compile
23
+ from fastled.sketch import looks_like_fastled_repo, looks_like_sketch_directory
24
+ from fastled.web_compile import (
25
+ SERVER_PORT,
26
+ ConnectionResult,
27
+ find_good_connection,
28
+ web_compile,
29
+ )
25
30
 
26
31
  machine = platform.machine().lower()
27
32
  IS_ARM: bool = "arm" in machine or "aarch64" in machine
@@ -44,12 +49,15 @@ DOCKER = DockerManager(container_name=CONTAINER_NAME)
44
49
 
45
50
  def parse_args() -> argparse.Namespace:
46
51
  """Parse command-line arguments."""
47
- parser = argparse.ArgumentParser(description="FastLED WASM Compiler")
52
+ parser = argparse.ArgumentParser(description=f"FastLED WASM Compiler {__version__}")
53
+ parser.add_argument(
54
+ "--version", action="version", version=f"%(prog)s {__version__}"
55
+ )
48
56
  parser.add_argument(
49
57
  "directory",
50
58
  type=str,
51
59
  nargs="?",
52
- default=os.getcwd(),
60
+ default=None,
53
61
  help="Directory containing the FastLED sketch to compile",
54
62
  )
55
63
  parser.add_argument(
@@ -57,11 +65,6 @@ def parse_args() -> argparse.Namespace:
57
65
  action="store_true",
58
66
  help="Just compile, skip opening the browser and watching for changes.",
59
67
  )
60
- parser.add_argument(
61
- "--no-auto-clean",
62
- action="store_true",
63
- help="Big performance gains for compilation, but it's flaky at this time",
64
- )
65
68
  parser.add_argument(
66
69
  "--web",
67
70
  "-w",
@@ -71,17 +74,6 @@ def parse_args() -> argparse.Namespace:
71
74
  const=DEFAULT_URL, # Default value when --web is specified without value
72
75
  help="Use web compiler. Optional URL can be provided (default: https://fastled.onrender.com)",
73
76
  )
74
- parser.add_argument(
75
- "--reuse",
76
- action="store_true",
77
- help="Reuse the existing container if it exists. (Not available with --web)",
78
- )
79
- parser.add_argument(
80
- "--exclude",
81
- type=str,
82
- nargs="+",
83
- help="Additional patterns to exclude from file watching (Not available with --web)",
84
- )
85
77
  parser.add_argument(
86
78
  "-i",
87
79
  "--interactive",
@@ -104,6 +96,12 @@ def parse_args() -> argparse.Namespace:
104
96
  build_mode.add_argument(
105
97
  "--release", action="store_true", help="Build in release mode"
106
98
  )
99
+ build_mode.add_argument(
100
+ "--localhost",
101
+ "--local",
102
+ action="store_true",
103
+ help="Use localhost for web compilation from an instance of fastled --server, creating it if necessary",
104
+ )
107
105
  build_mode.add_argument(
108
106
  "--server",
109
107
  action="store_true",
@@ -116,14 +114,30 @@ def parse_args() -> argparse.Namespace:
116
114
  help="Skips the test to see if the current directory is a valid FastLED sketch directory",
117
115
  )
118
116
 
119
- args = parser.parse_args()
117
+ cwd_is_fastled = looks_like_fastled_repo(Path(os.getcwd()))
120
118
 
121
- # Handle --web implications
122
- if args.web:
123
- if args.reuse:
124
- parser.error("--reuse cannot be used with --web")
125
- if args.exclude:
126
- parser.error("--exclude cannot be used with --web")
119
+ args = parser.parse_args()
120
+ if not cwd_is_fastled and not args.localhost and not args.web and not args.server:
121
+ print(f"Using web compiler at {DEFAULT_URL}")
122
+ args.web = DEFAULT_URL
123
+ if cwd_is_fastled and not args.web:
124
+ print("Forcing --local mode because we are in the FastLED repo")
125
+ args.localhost = True
126
+ if args.localhost:
127
+ args.web = "localhost"
128
+ if args.interactive and not args.server:
129
+ print("--interactive forces --server mode")
130
+ args.server = True
131
+ if args.directory is None and not args.server:
132
+ # does current directory look like a sketch?
133
+ maybe_sketch_dir = Path(os.getcwd())
134
+ if looks_like_sketch_directory(maybe_sketch_dir):
135
+ args.directory = str(maybe_sketch_dir)
136
+ else:
137
+ print(
138
+ "\nYou either need to specify a sketch directory or run in --server mode."
139
+ )
140
+ sys.exit(1)
127
141
 
128
142
  return args
129
143
 
@@ -188,16 +202,33 @@ def run_web_compiler(
188
202
 
189
203
 
190
204
  def _try_start_server_or_get_url(args: argparse.Namespace) -> str | CompileServer:
191
- if args.web:
205
+ is_local_host = "localhost" in args.web or "127.0.0.1" in args.web or args.localhost
206
+
207
+ # test to see if there is already a local host server
208
+ local_host_needs_server = False
209
+ if is_local_host:
210
+ addr = "localhost" if args.localhost else args.web
211
+ urls = [addr]
212
+ if ":" not in addr:
213
+ urls.append(f"{addr}:{SERVER_PORT}")
214
+
215
+ result: ConnectionResult | None = find_good_connection(urls)
216
+ if result is not None:
217
+ print(f"Found local server at {result.host}")
218
+ return result.host
219
+ else:
220
+ local_host_needs_server = True
221
+
222
+ if not local_host_needs_server and args.web:
192
223
  if isinstance(args.web, str):
193
224
  return args.web
194
225
  if isinstance(args.web, bool):
195
226
  return DEFAULT_URL
196
227
  return args.web
197
228
  else:
198
- disable_auto_clean = args.no_auto_clean
199
229
  try:
200
- compile_server = CompileServer(disable_auto_clean=disable_auto_clean)
230
+ print("No local server found, starting one...")
231
+ compile_server = CompileServer()
201
232
  print("Waiting for the local compiler to start...")
202
233
  if not compile_server.wait_for_startup():
203
234
  print("Failed to start local compiler.")
@@ -210,42 +241,11 @@ def _try_start_server_or_get_url(args: argparse.Namespace) -> str | CompileServe
210
241
  return DEFAULT_URL
211
242
 
212
243
 
213
- def _lots_and_lots_of_files(directory: Path) -> bool:
214
- return len(get_sketch_files(directory)) > 100
215
-
216
-
217
- def _looks_like_sketch_directory(directory: Path) -> bool:
218
- if looks_like_fastled_repo(directory):
219
- print("Directory looks like the FastLED repo")
220
- return False
221
-
222
- if _lots_and_lots_of_files(directory):
223
- print("Too many files in the directory, bailing out")
224
- return False
225
-
226
- # walk the path and if there are over 30 files, return False
227
- # at the root of the directory there should either be an ino file or a src directory
228
- # or some cpp files
229
- # if there is a platformio.ini file, return True
230
- ino_file_at_root = list(directory.glob("*.ino"))
231
- if ino_file_at_root:
232
- return True
233
- cpp_file_at_root = list(directory.glob("*.cpp"))
234
- if cpp_file_at_root:
235
- return True
236
- platformini_file = list(directory.glob("platformio.ini"))
237
- if platformini_file:
238
- return True
239
- return False
240
-
241
-
242
244
  def run_client(args: argparse.Namespace) -> int:
243
245
  compile_server: CompileServer | None = None
244
- open_web_browser = not args.just_compile
246
+ open_web_browser = not args.just_compile and not args.interactive
245
247
  profile = args.profile
246
- if not args.force_compile and not _looks_like_sketch_directory(
247
- Path(args.directory)
248
- ):
248
+ if not args.force_compile and not looks_like_sketch_directory(Path(args.directory)):
249
249
  print(
250
250
  "Error: Not a valid FastLED sketch directory, if you are sure it is, use --force-compile"
251
251
  )
@@ -324,40 +324,76 @@ def run_client(args: argparse.Namespace) -> int:
324
324
  compile_server.stop()
325
325
  return 1
326
326
 
327
- # Watch mode
328
327
  print("\nWatching for changes. Press Ctrl+C to stop...")
329
- watcher = FileChangedNotifier(args.directory, excluded_patterns=["fastled_js"])
330
- watcher.start()
328
+ sketch_filewatcher = FileWatcherProcess(
329
+ args.directory, excluded_patterns=["fastled_js"]
330
+ )
331
+
332
+ source_code_watcher: FileWatcherProcess | None = None
333
+ if compile_server and compile_server.using_fastled_src_dir_volume():
334
+ assert compile_server.fastled_src_dir is not None
335
+ source_code_watcher = FileWatcherProcess(
336
+ compile_server.fastled_src_dir, excluded_patterns=[]
337
+ )
338
+
339
+ def trigger_rebuild_if_sketch_changed(
340
+ last_compiled_result: CompiledResult,
341
+ ) -> CompiledResult:
342
+ changed_files = sketch_filewatcher.get_all_changes()
343
+ if changed_files:
344
+ print(f"\nChanges detected in {changed_files}")
345
+ last_hash_value = last_compiled_result.hash_value
346
+ out = compile_function(last_hash_value=last_hash_value)
347
+ if not out.success:
348
+ print("\nRecompilation failed.")
349
+ else:
350
+ print("\nRecompilation successful.")
351
+ return out
352
+ return last_compiled_result
331
353
 
332
354
  try:
333
355
  while True:
334
- try:
335
- changed_files = watcher.get_all_changes()
336
- except KeyboardInterrupt:
337
- print("\nExiting from watcher...")
338
- raise
339
- except Exception as e:
340
- print(f"Error getting changes: {e}")
341
- changed_files = []
342
- if changed_files:
343
- print(f"\nChanges detected in {changed_files}")
344
- last_hash_value = last_compiled_result.hash_value
345
- result = compile_function(last_hash_value=last_hash_value)
346
- if not result.success:
347
- print("\nRecompilation failed.")
348
- else:
349
- print("\nRecompilation successful.")
350
- time.sleep(0.3)
356
+ last_compiled_result = trigger_rebuild_if_sketch_changed(
357
+ last_compiled_result
358
+ )
359
+ if compile_server and not compile_server.proceess_running():
360
+ print("Server process is not running. Exiting...")
361
+ return 1
362
+ if source_code_watcher is not None:
363
+ changed_files = source_code_watcher.get_all_changes()
364
+ # de-duplicate changes
365
+ changed_files = sorted(list(set(changed_files)))
366
+ if changed_files:
367
+ print(f"\nChanges detected in FastLED source code: {changed_files}")
368
+ print("Press space bar to trigger compile.")
369
+
370
+ space_key_watcher = SpaceBarWatcher()
371
+ try:
372
+ while True:
373
+ if space_key_watcher.space_bar_pressed():
374
+ print("Space bar pressed, triggering recompile...")
375
+ last_compiled_result = compile_function(
376
+ last_hash_value=None
377
+ )
378
+ print("Finished recompile.")
379
+ break
380
+ elif len(sketch_filewatcher.get_all_changes()) > 0:
381
+ last_compiled_result = compile_function(
382
+ last_hash_value=None
383
+ )
384
+ break
385
+ time.sleep(0.1)
386
+ finally:
387
+ space_key_watcher.stop()
388
+
351
389
  except KeyboardInterrupt:
352
- watcher.stop()
353
390
  print("\nStopping watch mode...")
354
391
  return 0
355
392
  except Exception as e:
356
- watcher.stop()
357
393
  print(f"Error: {e}")
358
394
  return 1
359
395
  finally:
360
- watcher.stop()
396
+ sketch_filewatcher.stop()
361
397
  if compile_server:
362
398
  compile_server.stop()
363
399
  if browser_proc:
@@ -366,11 +402,9 @@ def run_client(args: argparse.Namespace) -> int:
366
402
 
367
403
  def run_server(args: argparse.Namespace) -> int:
368
404
  interactive = args.interactive
369
- compile_server = CompileServer(
370
- disable_auto_clean=args.no_auto_clean, interactive=interactive
371
- )
372
- print(f"Server started at {compile_server.url()}")
373
- compile_server.start()
405
+ compile_server = CompileServer(interactive=interactive)
406
+ if not interactive:
407
+ print(f"Server started at {compile_server.url()}")
374
408
  compile_server.wait_for_startup()
375
409
  try:
376
410
  while True:
@@ -388,13 +422,7 @@ def run_server(args: argparse.Namespace) -> int:
388
422
 
389
423
  def main() -> int:
390
424
  args = parse_args()
391
- target_dir = Path(args.directory)
392
- cwd_is_target_dir = target_dir == Path(os.getcwd())
393
- force_server = cwd_is_target_dir and looks_like_fastled_repo(target_dir)
394
- auto_server = (args.server or args.interactive or cwd_is_target_dir) and (
395
- not args.web and not args.just_compile
396
- )
397
- if auto_server or force_server:
425
+ if args.server:
398
426
  print("Running in server only mode.")
399
427
  return run_server(args)
400
428
  else:
@@ -404,9 +432,9 @@ def main() -> int:
404
432
 
405
433
  if __name__ == "__main__":
406
434
  try:
407
- sys.argv.append("examples/wasm")
408
- sys.argv.append("-w")
409
- sys.argv.append("localhost")
435
+ # os.chdir("../fastled")
436
+ sys.argv.append("examples/SdCard")
437
+ sys.argv.append("--localhost")
410
438
  sys.exit(main())
411
439
  except KeyboardInterrupt:
412
440
  print("\nExiting from main...")