fastled 1.0.8__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/paths.py ADDED
@@ -0,0 +1,4 @@
1
+ from pathlib import Path
2
+
3
+ HERE = Path(__file__).resolve().parent
4
+ PROJECT_ROOT = HERE.parent.parent
fastled/sketch.py ADDED
@@ -0,0 +1,55 @@
1
+ import os
2
+ from pathlib import Path
3
+
4
+
5
+ def get_sketch_files(directory: Path) -> list[Path]:
6
+ files: list[Path] = []
7
+ for root, dirs, filenames in os.walk(directory):
8
+ # ignore hidden directories
9
+ dirs[:] = [d for d in dirs if not d.startswith(".")]
10
+ # ignore fastled_js directory
11
+ dirs[:] = [d for d in dirs if "fastled_js" not in d]
12
+ # ignore hidden files
13
+ filenames = [f for f in filenames if not f.startswith(".")]
14
+ for filename in filenames:
15
+ if "platformio.ini" in filename:
16
+ continue
17
+ files.append(Path(root) / filename)
18
+ return files
19
+
20
+
21
+ def looks_like_fastled_repo(directory: Path) -> bool:
22
+ libprops = directory / "library.properties"
23
+ if not libprops.exists():
24
+ return False
25
+ txt = libprops.read_text(encoding="utf-8", errors="ignore")
26
+ return "FastLED" in txt
27
+
28
+
29
+ def _lots_and_lots_of_files(directory: Path) -> bool:
30
+ return len(get_sketch_files(directory)) > 100
31
+
32
+
33
+ def looks_like_sketch_directory(directory: Path) -> bool:
34
+ if looks_like_fastled_repo(directory):
35
+ print("Directory looks like the FastLED repo")
36
+ return False
37
+
38
+ if _lots_and_lots_of_files(directory):
39
+ print("Too many files in the directory, bailing out")
40
+ return False
41
+
42
+ # walk the path and if there are over 30 files, return False
43
+ # at the root of the directory there should either be an ino file or a src directory
44
+ # or some cpp files
45
+ # if there is a platformio.ini file, return True
46
+ ino_file_at_root = list(directory.glob("*.ino"))
47
+ if ino_file_at_root:
48
+ return True
49
+ cpp_file_at_root = list(directory.glob("*.cpp"))
50
+ if cpp_file_at_root:
51
+ return True
52
+ platformini_file = list(directory.glob("platformio.ini"))
53
+ if platformini_file:
54
+ return True
55
+ return False
fastled/web_compile.py ADDED
@@ -0,0 +1,227 @@
1
+ import io
2
+ import shutil
3
+ import tempfile
4
+ import zipfile
5
+ from concurrent.futures import ThreadPoolExecutor, as_completed
6
+ from dataclasses import dataclass
7
+ from pathlib import Path
8
+
9
+ import httpx
10
+
11
+ from fastled.build_mode import BuildMode
12
+ from fastled.compile_server import SERVER_PORT
13
+ from fastled.sketch import get_sketch_files
14
+
15
+ DEFAULT_HOST = "https://fastled.onrender.com"
16
+ ENDPOINT_COMPILED_WASM = "compile/wasm"
17
+ _TIMEOUT = 60 * 4 # 2 mins timeout
18
+ _AUTH_TOKEN = "oBOT5jbsO4ztgrpNsQwlmFLIKB"
19
+
20
+ _THREAD_POOL = ThreadPoolExecutor(max_workers=8)
21
+
22
+
23
+ @dataclass
24
+ class TestConnectionResult:
25
+ host: str
26
+ success: bool
27
+ ipv4: bool
28
+
29
+
30
+ @dataclass
31
+ class WebCompileResult:
32
+ success: bool
33
+ stdout: str
34
+ hash_value: str | None
35
+ zip_bytes: bytes
36
+
37
+ def __bool__(self) -> bool:
38
+ return self.success
39
+
40
+
41
+ def _sanitize_host(host: str) -> str:
42
+ if host.startswith("http"):
43
+ return host
44
+ is_local_host = "localhost" in host or "127.0.0.1" in host or "0.0.0.0" in host
45
+ use_https = not is_local_host
46
+ if use_https:
47
+ return host if host.startswith("https://") else f"https://{host}"
48
+ return host if host.startswith("http://") else f"http://{host}"
49
+
50
+
51
+ _CONNECTION_ERROR_MAP: dict[str, TestConnectionResult] = {}
52
+
53
+
54
+ def _test_connection(host: str, use_ipv4: bool) -> TestConnectionResult:
55
+ key = f"{host}-{use_ipv4}"
56
+ maybe_result: TestConnectionResult | None = _CONNECTION_ERROR_MAP.get(key)
57
+ if maybe_result is not None:
58
+ return maybe_result
59
+ transport = httpx.HTTPTransport(local_address="0.0.0.0") if use_ipv4 else None
60
+ try:
61
+ with httpx.Client(
62
+ timeout=_TIMEOUT,
63
+ transport=transport,
64
+ ) as test_client:
65
+ test_response = test_client.get(
66
+ f"{host}/healthz", timeout=3, follow_redirects=True
67
+ )
68
+ result = TestConnectionResult(
69
+ host, test_response.status_code == 200, use_ipv4
70
+ )
71
+ _CONNECTION_ERROR_MAP[key] = result
72
+ except Exception:
73
+ result = TestConnectionResult(host, False, use_ipv4)
74
+ _CONNECTION_ERROR_MAP[key] = result
75
+ return result
76
+
77
+
78
+ def zip_files(directory: Path) -> bytes | Exception:
79
+ print("Zipping files...")
80
+ try:
81
+ files = get_sketch_files(directory)
82
+ if not files:
83
+ raise FileNotFoundError(f"No files found in {directory}")
84
+ for f in files:
85
+ print(f"Adding file: {f}")
86
+ # Create in-memory zip file
87
+ zip_buffer = io.BytesIO()
88
+ with zipfile.ZipFile(
89
+ zip_buffer, "w", zipfile.ZIP_DEFLATED, compresslevel=9
90
+ ) as zip_file:
91
+ for file_path in files:
92
+ relative_path = file_path.relative_to(directory)
93
+ zip_file.write(file_path, str(Path("wasm") / relative_path))
94
+ return zip_buffer.getvalue()
95
+ except Exception as e:
96
+ return e
97
+
98
+
99
+ def web_compile(
100
+ directory: Path,
101
+ host: str | None = None,
102
+ auth_token: str | None = None,
103
+ build_mode: BuildMode | None = None,
104
+ profile: bool = False,
105
+ ) -> WebCompileResult:
106
+ host = _sanitize_host(host or DEFAULT_HOST)
107
+ print("Compiling on", host)
108
+ auth_token = auth_token or _AUTH_TOKEN
109
+
110
+ if not directory.exists():
111
+ raise FileNotFoundError(f"Directory not found: {directory}")
112
+
113
+ zip_bytes = zip_files(directory)
114
+ if isinstance(zip_bytes, Exception):
115
+ return WebCompileResult(
116
+ success=False, stdout=str(zip_bytes), hash_value=None, zip_bytes=b""
117
+ )
118
+ archive_size = len(zip_bytes)
119
+ print(f"Web compiling on {host}...")
120
+ try:
121
+
122
+ files = {"file": ("wasm.zip", zip_bytes, "application/x-zip-compressed")}
123
+ urls = [host]
124
+ domain = host.split("://")[-1]
125
+ if ":" not in domain:
126
+ urls.append(f"{host}:{SERVER_PORT}")
127
+ test_connection_result: TestConnectionResult | None = None
128
+
129
+ futures: list = []
130
+ ip_versions = [True, False] if "localhost" not in host else [True]
131
+ for ipv4 in ip_versions:
132
+ for url in urls:
133
+ f = _THREAD_POOL.submit(_test_connection, url, ipv4)
134
+ futures.append(f)
135
+
136
+ succeeded = False
137
+ for future in as_completed(futures):
138
+ result: TestConnectionResult = future.result()
139
+
140
+ if result.success:
141
+ print(f"Connection successful to {result.host}")
142
+ succeeded = True
143
+ # host = test_url
144
+ test_connection_result = result
145
+ break
146
+ else:
147
+ print(f"Ignoring {result.host} due to connection failure")
148
+
149
+ if not succeeded:
150
+ print("Connection failed to all endpoints")
151
+ return WebCompileResult(
152
+ success=False,
153
+ stdout="Connection failed",
154
+ hash_value=None,
155
+ zip_bytes=b"",
156
+ )
157
+ assert test_connection_result is not None
158
+ ipv4_stmt = "IPv4" if test_connection_result.ipv4 else "IPv6"
159
+ transport = (
160
+ httpx.HTTPTransport(local_address="0.0.0.0")
161
+ if test_connection_result.ipv4
162
+ else None
163
+ )
164
+ with httpx.Client(
165
+ transport=transport,
166
+ timeout=_TIMEOUT,
167
+ ) as client:
168
+ headers = {
169
+ "accept": "application/json",
170
+ "authorization": auth_token,
171
+ "build": (
172
+ build_mode.value.lower()
173
+ if build_mode
174
+ else BuildMode.QUICK.value.lower()
175
+ ),
176
+ "profile": "true" if profile else "false",
177
+ }
178
+
179
+ url = f"{test_connection_result.host}/{ENDPOINT_COMPILED_WASM}"
180
+ print(f"Compiling on {url} via {ipv4_stmt}. Zip size: {archive_size} bytes")
181
+ response = client.post(
182
+ url,
183
+ follow_redirects=True,
184
+ files=files,
185
+ headers=headers,
186
+ timeout=_TIMEOUT,
187
+ )
188
+
189
+ if response.status_code != 200:
190
+ json_response = response.json()
191
+ detail = json_response.get("detail", "Could not compile")
192
+ return WebCompileResult(
193
+ success=False, stdout=detail, hash_value=None, zip_bytes=b""
194
+ )
195
+
196
+ print(f"Response status code: {response}")
197
+ # Create a temporary directory to extract the zip
198
+ with tempfile.TemporaryDirectory() as extract_dir:
199
+ extract_path = Path(extract_dir)
200
+
201
+ # Write the response content to a temporary zip file
202
+ temp_zip = extract_path / "response.zip"
203
+ temp_zip.write_bytes(response.content)
204
+
205
+ # Extract the zip
206
+ shutil.unpack_archive(temp_zip, extract_path, "zip")
207
+
208
+ # Read stdout from out.txt if it exists
209
+ stdout_file = extract_path / "out.txt"
210
+ hash_file = extract_path / "hash.txt"
211
+ stdout = stdout_file.read_text() if stdout_file.exists() else ""
212
+ hash_value = hash_file.read_text() if hash_file.exists() else None
213
+
214
+ return WebCompileResult(
215
+ success=True,
216
+ stdout=stdout,
217
+ hash_value=hash_value,
218
+ zip_bytes=response.content,
219
+ )
220
+ except KeyboardInterrupt:
221
+ print("Keyboard interrupt")
222
+ raise
223
+ except httpx.HTTPError as e:
224
+ print(f"Error: {e}")
225
+ return WebCompileResult(
226
+ success=False, stdout=str(e), hash_value=None, zip_bytes=b""
227
+ )
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2019 zackees
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,121 @@
1
+ Metadata-Version: 2.1
2
+ Name: fastled
3
+ Version: 1.0.8
4
+ Summary: FastLED Wasm Compiler
5
+ Home-page: https://github.com/zackees/fastled-wasm
6
+ Maintainer: Zachary Vorhies
7
+ License: BSD 3-Clause License
8
+ Keywords: template-python-cmd
9
+ Classifier: Programming Language :: Python :: 3
10
+ Requires-Python: >=3.7
11
+ Description-Content-Type: text/markdown
12
+ License-File: LICENSE
13
+ Requires-Dist: docker
14
+ Requires-Dist: httpx
15
+ Requires-Dist: watchdog
16
+ Requires-Dist: livereload
17
+ Requires-Dist: download
18
+ Requires-Dist: filelock
19
+
20
+ # FastLED wasm compiler
21
+
22
+ Compiles an Arduino/Platformio sketch into a wasm binary that can be run directly in the web browser.
23
+
24
+ [![Linting](https://github.com/zackees/fastled-wasm/actions/workflows/lint.yml/badge.svg)](https://github.com/zackees/fastled-wasm/actions/workflows/lint.yml)
25
+ [![Build and Push Multi Docker Image](https://github.com/zackees/fastled-wasm/actions/workflows/build_multi_docker_image.yml/badge.svg)](https://github.com/zackees/fastled-wasm/actions/workflows/build_multi_docker_image.yml)
26
+ [![MacOS_Tests](https://github.com/zackees/fastled-wasm/actions/workflows/test_macos.yml/badge.svg)](https://github.com/zackees/fastled-wasm/actions/workflows/test_macos.yml)
27
+ [![Ubuntu_Tests](https://github.com/zackees/fastled-wasm/actions/workflows/test_ubuntu.yml/badge.svg)](https://github.com/zackees/fastled-wasm/actions/workflows/test_ubuntu.yml)
28
+ [![Win_Tests](https://github.com/zackees/fastled-wasm/actions/workflows/test_win.yml/badge.svg)](https://github.com/zackees/fastled-wasm/actions/workflows/test_win.yml)
29
+
30
+
31
+ # About
32
+
33
+ This python app will compile your FastLED style sketches into html/js/wasm output that runs directly in the browser.
34
+
35
+ Compile times are extremely fast - I've seen as low as 5 seconds but 8-15 seconds is typical.
36
+
37
+ This works on Windows/Linux/Mac(arm/x64).
38
+
39
+ Docker is required.
40
+
41
+ https://github.com/user-attachments/assets/64ae0e6c-5f8b-4830-ab87-dcc25bc61218
42
+
43
+ # Demo
44
+
45
+ https://zackees.github.io/fastled-wasm/
46
+
47
+
48
+
49
+ # Install
50
+
51
+ ```bash
52
+ pip install fastled-wasm
53
+ ```
54
+
55
+ **Note that you may need to install x86 docker emulation on Mac-m1 and later, as this is an x86 only image at the prsent.**
56
+
57
+ # Use
58
+
59
+ Change to the directory where the sketch lives and run
60
+
61
+ ```bash
62
+ cd <SKETCH-DIRECTORY>
63
+ fastled-wasm
64
+ ```
65
+
66
+ Or if you don't have docker then use our web compiler
67
+
68
+ ```bash
69
+ cd <SKETCH-DIRECTORY>
70
+ fastled-wasm --web
71
+ ```
72
+
73
+ After compilation a web browser windows will pop up.
74
+
75
+ # Hot reload by default
76
+
77
+ Once launched, the compiler will remain open, listening to changes and recompiling as necessary and hot-reloading the sketch into the current browser.
78
+
79
+ This style of development should be familiar to those doing web development.
80
+
81
+ # Data
82
+
83
+ If you want to embed data, then do so in the `data/` directory at the project root. The files will appear in the `data/` director of any spawned FileSystem or SDCard.
84
+
85
+
86
+ ### About the compilation.
87
+
88
+ Pre-processing is done to your source files. A fake Arduino.h will be inserted into your source files that will
89
+ provide shims for most of the common api points.
90
+
91
+
92
+
93
+ # Revisions
94
+
95
+ * 1.1.8 - Program now knows it's own version which will be displayed with help file. Use `--version` to get it directly.
96
+ * 1.1.7 - Sketch cache re-enabled, but selectively invalidated on cpp/h updates. Cleaned up deprecated args. Fixed double thread running for containers that was causing slowdown.
97
+ * 1.1.6 - Use the fast src volume map allow quick updates to fastled when developing on the source code.
98
+ * 1.1.5 - Filter out hidden files and directories from being included in the sketch archive sent to the compiler.
99
+ * 1.1.4 - Fix regression introduced by testing out ipv4/ipv6 connections from a thread pool.
100
+ * 1.1.3 - Live editing of *.h and *.cpp files is now possible. Sketch cache will be disabled in this mode.
101
+ * 1.1.2 - `--server` will now volume map fastled src directory if it detects this. This was also implemented on the docker side.
102
+ * 1.1.1 - `--interactive` is now supported to debug the container. Volume maps and better compatibilty with ipv4/v6 by concurrent connection finding.
103
+ * 1.1.0 - Use `fastled` as the command for the wasm compiler.
104
+ * 1.0.17 - Pulls updates when necessary. Removed dependency on keyring.
105
+ * 1.0.16 - `fastled-wasm` package name has been changed to `fled`
106
+ * 1.0.15 - `fled` is an alias of `fastled-wasm` and will eventually replace it. `--web-host` was folded into `--web`, which if unspecified will attempt to run a local docker server and fallback to the cloud server if that fails. Specifying `--web` with no arguments will default to the cloud server while an argument (like `localhost`) will cause it to bind to that already running server for compilation.
107
+ * 1.0.14 - For non significant changes (comments, whitespace) in C++/ino/*.h files, compilation is skipped. This significantly reduces load on the server and prevents unnecessary local client browser refreshes.
108
+ * 1.0.13 - Increase speed of local compiles by running the server version of the compiler so it can keep it's cache and not have to pay docker startup costs because now it's a persistant server until exit.
109
+ * 1.0.12 - Added suppport for compile modes. Pass in `--release`, `--quick`, `--debug` for different compile options. We also support `--profile` to profile the build process.
110
+ * 1.0.11 - `--web` compile will automatically be enabled if the local build using docker fails.
111
+ * 1.0.10 - Watching files is now available for `--web`
112
+ * 1.0.9 - Enabled web compile. Access it with `--web` or `--web-host`
113
+ * 1.0.8 - Allow more than one fastled-wasm browser instances to co-exist by searching for unused ports after 8081.
114
+ * 1.0.7 - Docker multi image build implemented, tool now points to new docker image compile.
115
+ * 1.0.6 - Removed `--no-open` and `--watch`, `--watch` is now assumed unless `--just-compile` is used.
116
+ * 1.0.5 - Implemented `--update` to update the compiler image from the docker registry.
117
+ * 1.0.4 - Implemented `--watch` which will watch for changes and then re-launch the compilation step.
118
+ * 1.0.3 - Integrated `live-server` to launch when available.
119
+ * 1.0.2 - Small bug with new installs.
120
+ * 1.0.1 - Re-use is no longer the default, due to problems.
121
+ * 1.0.0 - Initial release.
@@ -0,0 +1,19 @@
1
+ fastled/__init__.py,sha256=jl2oKYN0FYQt0z1v-aHeEqO9DGZIIYf3gUPuYhUrgKE,63
2
+ fastled/app.py,sha256=S6hDqYw8gLOOGpXr7pBFJ4opuqY0LhqrJiymajY6FWU,12875
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=SHKx9Dnt4EJiT-iPYakdPZBRIL1gsJGN9tY8FJW2myU,5079
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/web_compile.py,sha256=vVQ68lldGo3CaQcYGf3lh9yU6K_A3zP1gNJ29pi5J-E,7857
13
+ fastled/assets/example.txt,sha256=lTBovRjiz0_TgtAtbA1C5hNi2ffbqnNPqkKg6UiKCT8,54
14
+ fastled-1.0.8.dist-info/LICENSE,sha256=b6pOoifSXiUaz_lDS84vWlG3fr4yUKwB8fzkrH9R8bQ,1064
15
+ fastled-1.0.8.dist-info/METADATA,sha256=CimvtLSLl7xSQbSxb67Kdk0dRHTkvV-eDfulpjNvr0A,6316
16
+ fastled-1.0.8.dist-info/WHEEL,sha256=0VNUDWQJzfRahYI3neAhz2UVbRCtztpN5dPHAGvmGXc,109
17
+ fastled-1.0.8.dist-info/entry_points.txt,sha256=RCwmzCSOS4-C2i9EziANq7Z2Zb4KFnEMR1FQC0bBwAw,101
18
+ fastled-1.0.8.dist-info/top_level.txt,sha256=xfG6Z_ol9V5YmBROkZq2QTRwjbS2ouCUxaTJsOwfkOo,14
19
+ fastled-1.0.8.dist-info/RECORD,,
@@ -0,0 +1,6 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (75.5.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py2-none-any
5
+ Tag: py3-none-any
6
+
@@ -0,0 +1,4 @@
1
+ [console_scripts]
2
+ fastled = fastled.cli:main
3
+ fastled-wasm = fastled.cli:main
4
+ fled = fastled.cli:main
@@ -0,0 +1,2 @@
1
+ cache
2
+ fastled