fastled 1.3.28__py3-none-any.whl → 1.3.29__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/__version__.py CHANGED
@@ -1,6 +1,6 @@
1
1
  # IMPORTANT! There's a bug in github which will REJECT any version update
2
2
  # that has any other change in the repo. Please bump the version as the
3
3
  # ONLY change in a commit, or else the pypi update and the release will fail.
4
- __version__ = "1.3.28"
4
+ __version__ = "1.3.29"
5
5
 
6
6
  __version_url_latest__ = "https://raw.githubusercontent.com/zackees/fastled-wasm/refs/heads/main/src/fastled/__version__.py"
fastled/app.py CHANGED
@@ -1,177 +1,177 @@
1
- """
2
- Uses the latest wasm compiler image to compile the FastLED sketch.
3
- """
4
-
5
- import os
6
- import sys
7
- import time
8
- from pathlib import Path
9
-
10
- from fastled.client_server import run_client_server
11
- from fastled.compile_server import CompileServer
12
- from fastled.filewatcher import file_watcher_set
13
- from fastled.parse_args import Args, parse_args
14
- from fastled.sketch import find_sketch_directories, looks_like_fastled_repo
15
-
16
-
17
- def run_server(args: Args) -> int:
18
- interactive = args.interactive
19
- auto_update = args.auto_update
20
- mapped_dir = Path(args.directory).absolute() if args.directory else None
21
- if interactive and mapped_dir is None:
22
- print("Select a sketch when you enter interactive mode.")
23
- return 1
24
- compile_server = CompileServer(
25
- interactive=interactive,
26
- auto_updates=auto_update,
27
- mapped_dir=mapped_dir,
28
- auto_start=True,
29
- remove_previous=args.clear,
30
- )
31
-
32
- if not interactive:
33
- print(f"Server started at {compile_server.url()}")
34
- try:
35
- while True:
36
- if not compile_server.process_running():
37
- print("Server process is not running. Exiting...")
38
- return 1
39
- time.sleep(0.1)
40
- except KeyboardInterrupt:
41
- print("\nExiting from server...")
42
- return 1
43
- finally:
44
- compile_server.stop()
45
- return 0
46
-
47
-
48
- def main() -> int:
49
- from fastled import __version__
50
- from fastled.select_sketch_directory import select_sketch_directory
51
-
52
- args = parse_args()
53
- interactive: bool = args.interactive
54
- has_server = args.server
55
- update: bool = args.update
56
- build: bool = args.build
57
- just_compile: bool = args.just_compile
58
- # directory: Path | None = Path(args.directory).absolute() if args.directory else None
59
- directory: Path | None = Path(args.directory) if args.directory else None
60
- cwd_looks_like_fastled_repo = looks_like_fastled_repo()
61
-
62
- # now it is safe to print out the version
63
- print(f"FastLED version: {__version__}")
64
-
65
- # Resolve some of the last interactive arguments
66
- # 1. If interactive is set and the sketch directory is not given,
67
- # then prompt the user for a sketch directory.
68
- # 2. Tell the user they can use --server --interactive to
69
- # skip this prompt.
70
- if interactive and cwd_looks_like_fastled_repo and directory is None:
71
- answer = input(
72
- "No sketch directory selected, would you like to select one? (y/n): "
73
- )
74
- if answer.lower()[:1] == "y" or answer.lower() == "":
75
- sketch_list: list[Path] = find_sketch_directories()
76
- if sketch_list:
77
- maybe_dir: str | None = select_sketch_directory(
78
- sketch_list, cwd_looks_like_fastled_repo
79
- )
80
- if maybe_dir is not None:
81
- directory = Path(maybe_dir)
82
- if not directory.exists():
83
- print(
84
- f"Directory {directory} does not exist, entering interactive mode without project mapped in."
85
- )
86
- directory = None
87
-
88
- if update:
89
- # Force auto_update to ensure update check happens
90
- compile_server = CompileServer(interactive=False, auto_updates=True)
91
- compile_server.stop()
92
- print("Finished updating.")
93
- return 0
94
-
95
- if build:
96
- print("Building is disabled")
97
- build = False
98
-
99
- if interactive:
100
- # raise NotImplementedError("Building is not yet supported.")
101
- file_watcher_set(False)
102
- # project_root = Path(".").absolute()
103
- # print(f"Building Docker image at {project_root}")
104
- from fastled import Api
105
-
106
- server: CompileServer = CompileServer(
107
- interactive=interactive,
108
- auto_updates=False,
109
- mapped_dir=directory,
110
- auto_start=False,
111
- remove_previous=args.clear,
112
- )
113
-
114
- server.start(wait_for_startup=False)
115
-
116
- try:
117
- while server.process_running():
118
- # wait for ctrl-c
119
- time.sleep(0.1)
120
- except KeyboardInterrupt:
121
- print("\nExiting from server...")
122
- server.stop()
123
- return 0
124
-
125
- try:
126
- if interactive:
127
- server.stop()
128
- return 0
129
- print(f"Built Docker image: {server.name}")
130
- if not directory:
131
- if not directory:
132
- print("No directory specified")
133
- server.stop()
134
- return 0
135
-
136
- print("Running server")
137
-
138
- with Api.live_client(
139
- auto_updates=False,
140
- sketch_directory=directory,
141
- host=server,
142
- auto_start=True,
143
- keep_running=not just_compile,
144
- ) as _:
145
- while True:
146
- time.sleep(0.2) # wait for user to exit
147
- except KeyboardInterrupt:
148
- print("\nExiting from client...")
149
- server.stop()
150
- return 1
151
-
152
- if has_server:
153
- print("Running in server only mode.")
154
- return run_server(args)
155
- else:
156
- print("Running in client/server mode.")
157
- return run_client_server(args)
158
-
159
-
160
- if __name__ == "__main__":
161
- # Note that the entry point for the exe is in cli.py
162
- try:
163
- # sys.argv.append("-i")
164
- # sys.argv.append("-b")
165
- # sys.argv.append("examples/wasm")
166
- # sys.argv.append()
167
- import os
168
-
169
- os.chdir("../fastled")
170
- sys.argv.append("examples/FxWave2d")
171
- sys.exit(main())
172
- except KeyboardInterrupt:
173
- print("\nExiting from main...")
174
- sys.exit(1)
175
- except Exception as e:
176
- print(f"Error: {e}")
177
- sys.exit(1)
1
+ """
2
+ Uses the latest wasm compiler image to compile the FastLED sketch.
3
+ """
4
+
5
+ import os
6
+ import sys
7
+ import time
8
+ from pathlib import Path
9
+
10
+ from fastled.client_server import run_client_server
11
+ from fastled.compile_server import CompileServer
12
+ from fastled.filewatcher import file_watcher_set
13
+ from fastled.parse_args import Args, parse_args
14
+ from fastled.sketch import find_sketch_directories, looks_like_fastled_repo
15
+
16
+
17
+ def run_server(args: Args) -> int:
18
+ interactive = args.interactive
19
+ auto_update = args.auto_update
20
+ mapped_dir = Path(args.directory).absolute() if args.directory else None
21
+ if interactive and mapped_dir is None:
22
+ print("Select a sketch when you enter interactive mode.")
23
+ return 1
24
+ compile_server = CompileServer(
25
+ interactive=interactive,
26
+ auto_updates=auto_update,
27
+ mapped_dir=mapped_dir,
28
+ auto_start=True,
29
+ remove_previous=args.clear,
30
+ )
31
+
32
+ if not interactive:
33
+ print(f"Server started at {compile_server.url()}")
34
+ try:
35
+ while True:
36
+ if not compile_server.process_running():
37
+ print("Server process is not running. Exiting...")
38
+ return 1
39
+ time.sleep(0.1)
40
+ except KeyboardInterrupt:
41
+ print("\nExiting from server...")
42
+ return 1
43
+ finally:
44
+ compile_server.stop()
45
+ return 0
46
+
47
+
48
+ def main() -> int:
49
+ from fastled import __version__
50
+ from fastled.select_sketch_directory import select_sketch_directory
51
+
52
+ args = parse_args()
53
+ interactive: bool = args.interactive
54
+ has_server = args.server
55
+ update: bool = args.update
56
+ build: bool = args.build
57
+ just_compile: bool = args.just_compile
58
+ # directory: Path | None = Path(args.directory).absolute() if args.directory else None
59
+ directory: Path | None = Path(args.directory) if args.directory else None
60
+ cwd_looks_like_fastled_repo = looks_like_fastled_repo()
61
+
62
+ # now it is safe to print out the version
63
+ print(f"FastLED version: {__version__}")
64
+
65
+ # Resolve some of the last interactive arguments
66
+ # 1. If interactive is set and the sketch directory is not given,
67
+ # then prompt the user for a sketch directory.
68
+ # 2. Tell the user they can use --server --interactive to
69
+ # skip this prompt.
70
+ if interactive and cwd_looks_like_fastled_repo and directory is None:
71
+ answer = input(
72
+ "No sketch directory selected, would you like to select one? (y/n): "
73
+ )
74
+ if answer.lower()[:1] == "y" or answer.lower() == "":
75
+ sketch_list: list[Path] = find_sketch_directories()
76
+ if sketch_list:
77
+ maybe_dir: str | None = select_sketch_directory(
78
+ sketch_list, cwd_looks_like_fastled_repo
79
+ )
80
+ if maybe_dir is not None:
81
+ directory = Path(maybe_dir)
82
+ if not directory.exists():
83
+ print(
84
+ f"Directory {directory} does not exist, entering interactive mode without project mapped in."
85
+ )
86
+ directory = None
87
+
88
+ if update:
89
+ # Force auto_update to ensure update check happens
90
+ compile_server = CompileServer(interactive=False, auto_updates=True)
91
+ compile_server.stop()
92
+ print("Finished updating.")
93
+ return 0
94
+
95
+ if build:
96
+ print("Building is disabled")
97
+ build = False
98
+
99
+ if interactive:
100
+ # raise NotImplementedError("Building is not yet supported.")
101
+ file_watcher_set(False)
102
+ # project_root = Path(".").absolute()
103
+ # print(f"Building Docker image at {project_root}")
104
+ from fastled import Api
105
+
106
+ server: CompileServer = CompileServer(
107
+ interactive=interactive,
108
+ auto_updates=False,
109
+ mapped_dir=directory,
110
+ auto_start=False,
111
+ remove_previous=args.clear,
112
+ )
113
+
114
+ server.start(wait_for_startup=False)
115
+
116
+ try:
117
+ while server.process_running():
118
+ # wait for ctrl-c
119
+ time.sleep(0.1)
120
+ except KeyboardInterrupt:
121
+ print("\nExiting from server...")
122
+ server.stop()
123
+ return 0
124
+
125
+ try:
126
+ if interactive:
127
+ server.stop()
128
+ return 0
129
+ print(f"Built Docker image: {server.name}")
130
+ if not directory:
131
+ if not directory:
132
+ print("No directory specified")
133
+ server.stop()
134
+ return 0
135
+
136
+ print("Running server")
137
+
138
+ with Api.live_client(
139
+ auto_updates=False,
140
+ sketch_directory=directory,
141
+ host=server,
142
+ auto_start=True,
143
+ keep_running=not just_compile,
144
+ ) as _:
145
+ while True:
146
+ time.sleep(0.2) # wait for user to exit
147
+ except KeyboardInterrupt:
148
+ print("\nExiting from client...")
149
+ server.stop()
150
+ return 1
151
+
152
+ if has_server:
153
+ print("Running in server only mode.")
154
+ return run_server(args)
155
+ else:
156
+ print("Running in client/server mode.")
157
+ return run_client_server(args)
158
+
159
+
160
+ if __name__ == "__main__":
161
+ # Note that the entry point for the exe is in cli.py
162
+ try:
163
+ # sys.argv.append("-i")
164
+ # sys.argv.append("-b")
165
+ # sys.argv.append("examples/wasm")
166
+ # sys.argv.append()
167
+ import os
168
+
169
+ os.chdir("../fastled")
170
+ sys.argv.append("examples/FxWave2d")
171
+ sys.exit(main())
172
+ except KeyboardInterrupt:
173
+ print("\nExiting from main...")
174
+ sys.exit(1)
175
+ except Exception as e:
176
+ print(f"Error: {e}")
177
+ sys.exit(1)
fastled/open_browser.py CHANGED
@@ -1,137 +1,137 @@
1
- import atexit
2
- import random
3
- import sys
4
- import time
5
- import weakref
6
- from multiprocessing import Process
7
- from pathlib import Path
8
-
9
- from fastled.server_flask import run_flask_in_thread
10
-
11
- DEFAULT_PORT = 8089 # different than live version.
12
- PYTHON_EXE = sys.executable
13
-
14
- # Use a weak reference set to track processes without preventing garbage collection
15
- _WEAK_CLEANUP_SET = weakref.WeakSet()
16
-
17
-
18
- def add_cleanup(proc: Process) -> None:
19
- """Add a process to the cleanup list using weak references"""
20
- _WEAK_CLEANUP_SET.add(proc)
21
-
22
- # Register a cleanup function that checks if the process is still alive
23
- def cleanup_if_alive():
24
- if proc.is_alive():
25
- try:
26
- proc.terminate()
27
- proc.join(timeout=1.0)
28
- if proc.is_alive():
29
- proc.kill()
30
- except Exception:
31
- pass
32
-
33
- atexit.register(cleanup_if_alive)
34
-
35
-
36
- def is_port_free(port: int) -> bool:
37
- """Check if a port is free"""
38
- import httpx
39
-
40
- try:
41
- response = httpx.get(f"http://localhost:{port}", timeout=1)
42
- response.raise_for_status()
43
- return False
44
- except (httpx.HTTPError, httpx.ConnectError):
45
- return True
46
-
47
-
48
- def find_free_port(start_port: int) -> int:
49
- """Find a free port starting at start_port"""
50
- for port in range(start_port, start_port + 100, 2):
51
- if is_port_free(port):
52
- print(f"Found free port: {port}")
53
- return port
54
- else:
55
- print(f"Port {port} is in use, finding next")
56
- raise ValueError("Could not find a free port")
57
-
58
-
59
- def wait_for_server(port: int, timeout: int = 10) -> None:
60
- """Wait for the server to start."""
61
- from httpx import get
62
-
63
- future_time = time.time() + timeout
64
- while future_time > time.time():
65
- try:
66
- url = f"http://localhost:{port}"
67
- # print(f"Waiting for server to start at {url}")
68
- response = get(url, timeout=1)
69
- if response.status_code == 200:
70
- return
71
- except Exception:
72
- continue
73
- raise TimeoutError("Could not connect to server")
74
-
75
-
76
- def spawn_http_server(
77
- fastled_js: Path,
78
- compile_server_port: int,
79
- port: int | None = None,
80
- open_browser: bool = True,
81
- ) -> Process:
82
-
83
- if port is not None and not is_port_free(port):
84
- raise ValueError(f"Port {port} was specified but in use")
85
- if port is None:
86
- offset = random.randint(0, 100)
87
- port = find_free_port(DEFAULT_PORT + offset)
88
-
89
- # port: int,
90
- # cwd: Path,
91
- # compile_server_port: int,
92
- # certfile: Path | None = None,
93
- # keyfile: Path | None = None,
94
-
95
- proc = Process(
96
- target=run_flask_in_thread,
97
- args=(port, fastled_js, compile_server_port),
98
- daemon=True,
99
- )
100
- add_cleanup(proc)
101
- proc.start()
102
-
103
- # Add to cleanup set with weak reference
104
- add_cleanup(proc)
105
-
106
- wait_for_server(port)
107
- if open_browser:
108
- print(f"Opening browser to http://localhost:{port}")
109
- import webbrowser
110
-
111
- webbrowser.open(
112
- url=f"http://localhost:{port}",
113
- new=1,
114
- autoraise=True,
115
- )
116
- return proc
117
-
118
-
119
- if __name__ == "__main__":
120
- import argparse
121
-
122
- parser = argparse.ArgumentParser(
123
- description="Open a browser to the fastled_js directory"
124
- )
125
- parser.add_argument(
126
- "fastled_js", type=Path, help="Path to the fastled_js directory"
127
- )
128
- parser.add_argument(
129
- "--port",
130
- type=int,
131
- default=DEFAULT_PORT,
132
- help=f"Port to run the server on (default: {DEFAULT_PORT})",
133
- )
134
- args = parser.parse_args()
135
-
136
- proc = spawn_http_server(args.fastled_js, args.port, open_browser=True)
137
- proc.join()
1
+ import atexit
2
+ import random
3
+ import sys
4
+ import time
5
+ import weakref
6
+ from multiprocessing import Process
7
+ from pathlib import Path
8
+
9
+ from fastled.server_flask import run_flask_in_thread
10
+
11
+ DEFAULT_PORT = 8089 # different than live version.
12
+ PYTHON_EXE = sys.executable
13
+
14
+ # Use a weak reference set to track processes without preventing garbage collection
15
+ _WEAK_CLEANUP_SET = weakref.WeakSet()
16
+
17
+
18
+ def add_cleanup(proc: Process) -> None:
19
+ """Add a process to the cleanup list using weak references"""
20
+ _WEAK_CLEANUP_SET.add(proc)
21
+
22
+ # Register a cleanup function that checks if the process is still alive
23
+ def cleanup_if_alive():
24
+ if proc.is_alive():
25
+ try:
26
+ proc.terminate()
27
+ proc.join(timeout=1.0)
28
+ if proc.is_alive():
29
+ proc.kill()
30
+ except Exception:
31
+ pass
32
+
33
+ atexit.register(cleanup_if_alive)
34
+
35
+
36
+ def is_port_free(port: int) -> bool:
37
+ """Check if a port is free"""
38
+ import httpx
39
+
40
+ try:
41
+ response = httpx.get(f"http://localhost:{port}", timeout=1)
42
+ response.raise_for_status()
43
+ return False
44
+ except (httpx.HTTPError, httpx.ConnectError):
45
+ return True
46
+
47
+
48
+ def find_free_port(start_port: int) -> int:
49
+ """Find a free port starting at start_port"""
50
+ for port in range(start_port, start_port + 100, 2):
51
+ if is_port_free(port):
52
+ print(f"Found free port: {port}")
53
+ return port
54
+ else:
55
+ print(f"Port {port} is in use, finding next")
56
+ raise ValueError("Could not find a free port")
57
+
58
+
59
+ def wait_for_server(port: int, timeout: int = 10) -> None:
60
+ """Wait for the server to start."""
61
+ from httpx import get
62
+
63
+ future_time = time.time() + timeout
64
+ while future_time > time.time():
65
+ try:
66
+ url = f"http://localhost:{port}"
67
+ # print(f"Waiting for server to start at {url}")
68
+ response = get(url, timeout=1)
69
+ if response.status_code == 200:
70
+ return
71
+ except Exception:
72
+ continue
73
+ raise TimeoutError("Could not connect to server")
74
+
75
+
76
+ def spawn_http_server(
77
+ fastled_js: Path,
78
+ compile_server_port: int,
79
+ port: int | None = None,
80
+ open_browser: bool = True,
81
+ ) -> Process:
82
+
83
+ if port is not None and not is_port_free(port):
84
+ raise ValueError(f"Port {port} was specified but in use")
85
+ if port is None:
86
+ offset = random.randint(0, 100)
87
+ port = find_free_port(DEFAULT_PORT + offset)
88
+
89
+ # port: int,
90
+ # cwd: Path,
91
+ # compile_server_port: int,
92
+ # certfile: Path | None = None,
93
+ # keyfile: Path | None = None,
94
+
95
+ proc = Process(
96
+ target=run_flask_in_thread,
97
+ args=(port, fastled_js, compile_server_port),
98
+ daemon=True,
99
+ )
100
+ add_cleanup(proc)
101
+ proc.start()
102
+
103
+ # Add to cleanup set with weak reference
104
+ add_cleanup(proc)
105
+
106
+ wait_for_server(port)
107
+ if open_browser:
108
+ print(f"Opening browser to http://localhost:{port}")
109
+ import webbrowser
110
+
111
+ webbrowser.open(
112
+ url=f"http://localhost:{port}",
113
+ new=1,
114
+ autoraise=True,
115
+ )
116
+ return proc
117
+
118
+
119
+ if __name__ == "__main__":
120
+ import argparse
121
+
122
+ parser = argparse.ArgumentParser(
123
+ description="Open a browser to the fastled_js directory"
124
+ )
125
+ parser.add_argument(
126
+ "fastled_js", type=Path, help="Path to the fastled_js directory"
127
+ )
128
+ parser.add_argument(
129
+ "--port",
130
+ type=int,
131
+ default=DEFAULT_PORT,
132
+ help=f"Port to run the server on (default: {DEFAULT_PORT})",
133
+ )
134
+ args = parser.parse_args()
135
+
136
+ proc = spawn_http_server(args.fastled_js, args.port, open_browser=True)
137
+ proc.join()