fastled 1.0.11__py2.py3-none-any.whl → 1.0.12__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 +1 -1
- fastled/app.py +27 -6
- fastled/compile_server.py +6 -0
- fastled/web_compile.py +284 -277
- {fastled-1.0.11.dist-info → fastled-1.0.12.dist-info}/METADATA +2 -3
- {fastled-1.0.11.dist-info → fastled-1.0.12.dist-info}/RECORD +10 -10
- {fastled-1.0.11.dist-info → fastled-1.0.12.dist-info}/LICENSE +0 -0
- {fastled-1.0.11.dist-info → fastled-1.0.12.dist-info}/WHEEL +0 -0
- {fastled-1.0.11.dist-info → fastled-1.0.12.dist-info}/entry_points.txt +0 -0
- {fastled-1.0.11.dist-info → fastled-1.0.12.dist-info}/top_level.txt +0 -0
fastled/__init__.py
CHANGED
fastled/app.py
CHANGED
@@ -20,8 +20,8 @@ from fastled.docker_manager import DockerManager
|
|
20
20
|
from fastled.filewatcher import FileWatcherProcess
|
21
21
|
from fastled.keyboard import SpaceBarWatcher
|
22
22
|
from fastled.open_browser import open_browser_thread
|
23
|
-
from fastled.sketch import looks_like_sketch_directory
|
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 ConnectionResult, find_good_connection, web_compile
|
25
25
|
|
26
26
|
machine = platform.machine().lower()
|
27
27
|
IS_ARM: bool = "arm" in machine or "aarch64" in machine
|
@@ -108,13 +108,21 @@ def parse_args() -> argparse.Namespace:
|
|
108
108
|
help="Skips the test to see if the current directory is a valid FastLED sketch directory",
|
109
109
|
)
|
110
110
|
|
111
|
+
cwd_is_fastled = looks_like_fastled_repo(Path(os.getcwd()))
|
112
|
+
|
111
113
|
args = parser.parse_args()
|
114
|
+
if not cwd_is_fastled and not args.localhost and not args.web and not args.server:
|
115
|
+
print(f"Using web compiler at {DEFAULT_URL}")
|
116
|
+
args.web = DEFAULT_URL
|
117
|
+
if cwd_is_fastled and not args.web:
|
118
|
+
args.localhost = True
|
112
119
|
if args.localhost:
|
113
120
|
args.web = "localhost"
|
114
|
-
if args.web is not None:
|
115
|
-
args.web = args.web if args.web == "" else args.web
|
116
121
|
if args.server and args.web:
|
117
122
|
parser.error("--server and --web are mutually exclusive")
|
123
|
+
if args.interactive and not args.server:
|
124
|
+
print("--interactive forces --server mode")
|
125
|
+
args.server = True
|
118
126
|
if args.directory is None and not args.server:
|
119
127
|
# does current directory look like a sketch?
|
120
128
|
maybe_sketch_dir = Path(os.getcwd())
|
@@ -125,6 +133,7 @@ def parse_args() -> argparse.Namespace:
|
|
125
133
|
"\nYou either need to specify a sketch directory or run in --server mode."
|
126
134
|
)
|
127
135
|
sys.exit(1)
|
136
|
+
|
128
137
|
return args
|
129
138
|
|
130
139
|
|
@@ -188,7 +197,18 @@ def run_web_compiler(
|
|
188
197
|
|
189
198
|
|
190
199
|
def _try_start_server_or_get_url(args: argparse.Namespace) -> str | CompileServer:
|
191
|
-
|
200
|
+
is_local_host = "localhost" in args.web or "127.0.0.1" in args.web or args.localhost
|
201
|
+
|
202
|
+
# test to see if there is already a local host server
|
203
|
+
local_host_needs_server = False
|
204
|
+
if is_local_host:
|
205
|
+
addr = "localhost" if args.localhost else args.web
|
206
|
+
result: ConnectionResult | None = find_good_connection([addr])
|
207
|
+
if result is not None:
|
208
|
+
print(f"Found local server at {result.host}")
|
209
|
+
local_host_needs_server = True
|
210
|
+
|
211
|
+
if not local_host_needs_server and args.web:
|
192
212
|
if isinstance(args.web, str):
|
193
213
|
return args.web
|
194
214
|
if isinstance(args.web, bool):
|
@@ -196,6 +216,7 @@ def _try_start_server_or_get_url(args: argparse.Namespace) -> str | CompileServe
|
|
196
216
|
return args.web
|
197
217
|
else:
|
198
218
|
try:
|
219
|
+
print("No local server found, starting one...")
|
199
220
|
compile_server = CompileServer()
|
200
221
|
print("Waiting for the local compiler to start...")
|
201
222
|
if not compile_server.wait_for_startup():
|
@@ -211,7 +232,7 @@ def _try_start_server_or_get_url(args: argparse.Namespace) -> str | CompileServe
|
|
211
232
|
|
212
233
|
def run_client(args: argparse.Namespace) -> int:
|
213
234
|
compile_server: CompileServer | None = None
|
214
|
-
open_web_browser = not args.just_compile
|
235
|
+
open_web_browser = not args.just_compile and not args.interactive
|
215
236
|
profile = args.profile
|
216
237
|
if not args.force_compile and not looks_like_sketch_directory(Path(args.directory)):
|
217
238
|
print(
|
fastled/compile_server.py
CHANGED
@@ -48,6 +48,12 @@ class CompileServer:
|
|
48
48
|
self.fastled_src_dir: Path | None = fastled_src_dir
|
49
49
|
self.interactive = interactive
|
50
50
|
self._port = self._start()
|
51
|
+
# fancy print
|
52
|
+
if not interactive:
|
53
|
+
msg = f"# FastLED Compile Server started at {self.url()} #"
|
54
|
+
print("\n" + "#" * len(msg))
|
55
|
+
print(msg)
|
56
|
+
print("#" * len(msg) + "\n")
|
51
57
|
|
52
58
|
def using_fastled_src_dir_volume(self) -> bool:
|
53
59
|
return self.fastled_src_dir is not None
|
fastled/web_compile.py
CHANGED
@@ -1,277 +1,284 @@
|
|
1
|
-
import io
|
2
|
-
import json
|
3
|
-
import os
|
4
|
-
import shutil
|
5
|
-
import tempfile
|
6
|
-
import zipfile
|
7
|
-
from concurrent.futures import ProcessPoolExecutor, as_completed
|
8
|
-
from dataclasses import dataclass
|
9
|
-
from pathlib import Path
|
10
|
-
|
11
|
-
import httpx
|
12
|
-
|
13
|
-
from fastled.build_mode import BuildMode
|
14
|
-
from fastled.compile_server import SERVER_PORT
|
15
|
-
from fastled.sketch import get_sketch_files
|
16
|
-
from fastled.util import hash_file
|
17
|
-
|
18
|
-
DEFAULT_HOST = "https://fastled.onrender.com"
|
19
|
-
ENDPOINT_COMPILED_WASM = "compile/wasm"
|
20
|
-
_TIMEOUT = 60 * 4 # 2 mins timeout
|
21
|
-
_AUTH_TOKEN = "oBOT5jbsO4ztgrpNsQwlmFLIKB"
|
22
|
-
ENABLE_EMBEDDED_DATA = True
|
23
|
-
_EXECUTOR = ProcessPoolExecutor(max_workers=8)
|
24
|
-
|
25
|
-
|
26
|
-
@dataclass
|
27
|
-
class ConnectionResult:
|
28
|
-
host: str
|
29
|
-
success: bool
|
30
|
-
ipv4: bool
|
31
|
-
|
32
|
-
|
33
|
-
@dataclass
|
34
|
-
class WebCompileResult:
|
35
|
-
success: bool
|
36
|
-
stdout: str
|
37
|
-
hash_value: str | None
|
38
|
-
zip_bytes: bytes
|
39
|
-
|
40
|
-
def __bool__(self) -> bool:
|
41
|
-
return self.success
|
42
|
-
|
43
|
-
|
44
|
-
def _sanitize_host(host: str) -> str:
|
45
|
-
if host.startswith("http"):
|
46
|
-
return host
|
47
|
-
is_local_host = "localhost" in host or "127.0.0.1" in host or "0.0.0.0" in host
|
48
|
-
use_https = not is_local_host
|
49
|
-
if use_https:
|
50
|
-
return host if host.startswith("https://") else f"https://{host}"
|
51
|
-
return host if host.startswith("http://") else f"http://{host}"
|
52
|
-
|
53
|
-
|
54
|
-
def _test_connection(host: str, use_ipv4: bool) -> ConnectionResult:
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
return
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
)
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
"
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
)
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
)
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
1
|
+
import io
|
2
|
+
import json
|
3
|
+
import os
|
4
|
+
import shutil
|
5
|
+
import tempfile
|
6
|
+
import zipfile
|
7
|
+
from concurrent.futures import Future, ProcessPoolExecutor, as_completed
|
8
|
+
from dataclasses import dataclass
|
9
|
+
from pathlib import Path
|
10
|
+
|
11
|
+
import httpx
|
12
|
+
|
13
|
+
from fastled.build_mode import BuildMode
|
14
|
+
from fastled.compile_server import SERVER_PORT
|
15
|
+
from fastled.sketch import get_sketch_files
|
16
|
+
from fastled.util import hash_file
|
17
|
+
|
18
|
+
DEFAULT_HOST = "https://fastled.onrender.com"
|
19
|
+
ENDPOINT_COMPILED_WASM = "compile/wasm"
|
20
|
+
_TIMEOUT = 60 * 4 # 2 mins timeout
|
21
|
+
_AUTH_TOKEN = "oBOT5jbsO4ztgrpNsQwlmFLIKB"
|
22
|
+
ENABLE_EMBEDDED_DATA = True
|
23
|
+
_EXECUTOR = ProcessPoolExecutor(max_workers=8)
|
24
|
+
|
25
|
+
|
26
|
+
@dataclass
|
27
|
+
class ConnectionResult:
|
28
|
+
host: str
|
29
|
+
success: bool
|
30
|
+
ipv4: bool
|
31
|
+
|
32
|
+
|
33
|
+
@dataclass
|
34
|
+
class WebCompileResult:
|
35
|
+
success: bool
|
36
|
+
stdout: str
|
37
|
+
hash_value: str | None
|
38
|
+
zip_bytes: bytes
|
39
|
+
|
40
|
+
def __bool__(self) -> bool:
|
41
|
+
return self.success
|
42
|
+
|
43
|
+
|
44
|
+
def _sanitize_host(host: str) -> str:
|
45
|
+
if host.startswith("http"):
|
46
|
+
return host
|
47
|
+
is_local_host = "localhost" in host or "127.0.0.1" in host or "0.0.0.0" in host
|
48
|
+
use_https = not is_local_host
|
49
|
+
if use_https:
|
50
|
+
return host if host.startswith("https://") else f"https://{host}"
|
51
|
+
return host if host.startswith("http://") else f"http://{host}"
|
52
|
+
|
53
|
+
|
54
|
+
def _test_connection(host: str, use_ipv4: bool) -> ConnectionResult:
|
55
|
+
# Function static cache
|
56
|
+
connection_cache = _test_connection.__dict__.setdefault("_CONNECTION_CACHE", {})
|
57
|
+
key = f"{host}_{use_ipv4}"
|
58
|
+
cached_result = connection_cache.get(key)
|
59
|
+
if cached_result is not None:
|
60
|
+
return cached_result
|
61
|
+
transport = httpx.HTTPTransport(local_address="0.0.0.0") if use_ipv4 else None
|
62
|
+
try:
|
63
|
+
with httpx.Client(
|
64
|
+
timeout=_TIMEOUT,
|
65
|
+
transport=transport,
|
66
|
+
) as test_client:
|
67
|
+
test_response = test_client.get(
|
68
|
+
f"{host}/healthz", timeout=3, follow_redirects=True
|
69
|
+
)
|
70
|
+
result = ConnectionResult(host, test_response.status_code == 200, use_ipv4)
|
71
|
+
except Exception:
|
72
|
+
result = ConnectionResult(host, False, use_ipv4)
|
73
|
+
connection_cache[key] = result
|
74
|
+
return result
|
75
|
+
|
76
|
+
|
77
|
+
def _file_info(file_path: Path) -> str:
|
78
|
+
hash_txt = hash_file(file_path)
|
79
|
+
file_size = file_path.stat().st_size
|
80
|
+
json_str = json.dumps({"hash": hash_txt, "size": file_size})
|
81
|
+
return json_str
|
82
|
+
|
83
|
+
|
84
|
+
@dataclass
|
85
|
+
class ZipResult:
|
86
|
+
zip_bytes: bytes
|
87
|
+
zip_embedded_bytes: bytes | None
|
88
|
+
success: bool
|
89
|
+
error: str | None
|
90
|
+
|
91
|
+
|
92
|
+
def zip_files(directory: Path) -> ZipResult | Exception:
|
93
|
+
print("Zipping files...")
|
94
|
+
try:
|
95
|
+
files = get_sketch_files(directory)
|
96
|
+
if not files:
|
97
|
+
raise FileNotFoundError(f"No files found in {directory}")
|
98
|
+
for f in files:
|
99
|
+
print(f"Adding file: {f}")
|
100
|
+
# Create in-memory zip file
|
101
|
+
has_embedded_zip = False
|
102
|
+
zip_embedded_buffer = io.BytesIO()
|
103
|
+
zip_buffer = io.BytesIO()
|
104
|
+
with zipfile.ZipFile(
|
105
|
+
zip_embedded_buffer, "w", zipfile.ZIP_DEFLATED, compresslevel=9
|
106
|
+
) as emebedded_zip_file:
|
107
|
+
with zipfile.ZipFile(
|
108
|
+
zip_buffer, "w", zipfile.ZIP_DEFLATED, compresslevel=9
|
109
|
+
) as zip_file:
|
110
|
+
for file_path in files:
|
111
|
+
relative_path = file_path.relative_to(directory)
|
112
|
+
achive_path = str(Path("wasm") / relative_path)
|
113
|
+
if str(relative_path).startswith("data") and ENABLE_EMBEDDED_DATA:
|
114
|
+
_file_info_str = _file_info(file_path)
|
115
|
+
zip_file.writestr(
|
116
|
+
achive_path + ".embedded.json", _file_info_str
|
117
|
+
)
|
118
|
+
emebedded_zip_file.write(file_path, relative_path)
|
119
|
+
has_embedded_zip = True
|
120
|
+
else:
|
121
|
+
zip_file.write(file_path, achive_path)
|
122
|
+
result = ZipResult(
|
123
|
+
zip_bytes=zip_buffer.getvalue(),
|
124
|
+
zip_embedded_bytes=(
|
125
|
+
zip_embedded_buffer.getvalue() if has_embedded_zip else None
|
126
|
+
),
|
127
|
+
success=True,
|
128
|
+
error=None,
|
129
|
+
)
|
130
|
+
return result
|
131
|
+
except Exception as e:
|
132
|
+
return e
|
133
|
+
|
134
|
+
|
135
|
+
def find_good_connection(
|
136
|
+
urls: list[str], filter_out_bad=False
|
137
|
+
) -> ConnectionResult | None:
|
138
|
+
futures: list[Future] = []
|
139
|
+
ip_versions = [True, False]
|
140
|
+
# Start all connection tests
|
141
|
+
for ipv4 in ip_versions:
|
142
|
+
for url in urls:
|
143
|
+
f = _EXECUTOR.submit(_test_connection, url, ipv4)
|
144
|
+
futures.append(f)
|
145
|
+
|
146
|
+
try:
|
147
|
+
# Return first successful result
|
148
|
+
for future in as_completed(futures):
|
149
|
+
result: ConnectionResult = future.result()
|
150
|
+
if result.success or not filter_out_bad:
|
151
|
+
return result
|
152
|
+
finally:
|
153
|
+
# Cancel any remaining futures
|
154
|
+
for future in futures:
|
155
|
+
future.cancel()
|
156
|
+
return None
|
157
|
+
|
158
|
+
|
159
|
+
def web_compile(
|
160
|
+
directory: Path,
|
161
|
+
host: str | None = None,
|
162
|
+
auth_token: str | None = None,
|
163
|
+
build_mode: BuildMode | None = None,
|
164
|
+
profile: bool = False,
|
165
|
+
) -> WebCompileResult:
|
166
|
+
host = _sanitize_host(host or DEFAULT_HOST)
|
167
|
+
print("Compiling on", host)
|
168
|
+
auth_token = auth_token or _AUTH_TOKEN
|
169
|
+
if not directory.exists():
|
170
|
+
raise FileNotFoundError(f"Directory not found: {directory}")
|
171
|
+
zip_result = zip_files(directory)
|
172
|
+
if isinstance(zip_result, Exception):
|
173
|
+
return WebCompileResult(
|
174
|
+
success=False, stdout=str(zip_result), hash_value=None, zip_bytes=b""
|
175
|
+
)
|
176
|
+
zip_bytes = zip_result.zip_bytes
|
177
|
+
archive_size = len(zip_bytes)
|
178
|
+
print(f"Web compiling on {host}...")
|
179
|
+
try:
|
180
|
+
urls = [host]
|
181
|
+
domain = host.split("://")[-1]
|
182
|
+
if ":" not in domain:
|
183
|
+
urls.append(f"{host}:{SERVER_PORT}")
|
184
|
+
|
185
|
+
connection_result = find_good_connection(urls)
|
186
|
+
if connection_result is None:
|
187
|
+
print("Connection failed to all endpoints")
|
188
|
+
return WebCompileResult(
|
189
|
+
success=False,
|
190
|
+
stdout="Connection failed",
|
191
|
+
hash_value=None,
|
192
|
+
zip_bytes=b"",
|
193
|
+
)
|
194
|
+
|
195
|
+
ipv4_stmt = "IPv4" if connection_result.ipv4 else "IPv6"
|
196
|
+
transport = (
|
197
|
+
httpx.HTTPTransport(local_address="0.0.0.0")
|
198
|
+
if connection_result.ipv4
|
199
|
+
else None
|
200
|
+
)
|
201
|
+
with httpx.Client(
|
202
|
+
transport=transport,
|
203
|
+
timeout=_TIMEOUT,
|
204
|
+
) as client:
|
205
|
+
headers = {
|
206
|
+
"accept": "application/json",
|
207
|
+
"authorization": auth_token,
|
208
|
+
"build": (
|
209
|
+
build_mode.value.lower()
|
210
|
+
if build_mode
|
211
|
+
else BuildMode.QUICK.value.lower()
|
212
|
+
),
|
213
|
+
"profile": "true" if profile else "false",
|
214
|
+
}
|
215
|
+
|
216
|
+
url = f"{connection_result.host}/{ENDPOINT_COMPILED_WASM}"
|
217
|
+
print(f"Compiling on {url} via {ipv4_stmt}. Zip size: {archive_size} bytes")
|
218
|
+
files = {"file": ("wasm.zip", zip_bytes, "application/x-zip-compressed")}
|
219
|
+
response = client.post(
|
220
|
+
url,
|
221
|
+
follow_redirects=True,
|
222
|
+
files=files,
|
223
|
+
headers=headers,
|
224
|
+
timeout=_TIMEOUT,
|
225
|
+
)
|
226
|
+
|
227
|
+
if response.status_code != 200:
|
228
|
+
json_response = response.json()
|
229
|
+
detail = json_response.get("detail", "Could not compile")
|
230
|
+
return WebCompileResult(
|
231
|
+
success=False, stdout=detail, hash_value=None, zip_bytes=b""
|
232
|
+
)
|
233
|
+
|
234
|
+
print(f"Response status code: {response}")
|
235
|
+
# Create a temporary directory to extract the zip
|
236
|
+
with tempfile.TemporaryDirectory() as extract_dir:
|
237
|
+
extract_path = Path(extract_dir)
|
238
|
+
|
239
|
+
# Write the response content to a temporary zip file
|
240
|
+
temp_zip = extract_path / "response.zip"
|
241
|
+
temp_zip.write_bytes(response.content)
|
242
|
+
|
243
|
+
# Extract the zip
|
244
|
+
shutil.unpack_archive(temp_zip, extract_path, "zip")
|
245
|
+
|
246
|
+
if zip_result.zip_embedded_bytes:
|
247
|
+
# extract the embedded bytes, which were not sent to the server
|
248
|
+
temp_zip.write_bytes(zip_result.zip_embedded_bytes)
|
249
|
+
shutil.unpack_archive(temp_zip, extract_path, "zip")
|
250
|
+
|
251
|
+
# we don't need the temp zip anymore
|
252
|
+
temp_zip.unlink()
|
253
|
+
|
254
|
+
# Read stdout from out.txt if it exists
|
255
|
+
stdout_file = extract_path / "out.txt"
|
256
|
+
hash_file = extract_path / "hash.txt"
|
257
|
+
stdout = stdout_file.read_text() if stdout_file.exists() else ""
|
258
|
+
hash_value = hash_file.read_text() if hash_file.exists() else None
|
259
|
+
|
260
|
+
# now rezip the extracted files since we added the embedded json files
|
261
|
+
out_buffer = io.BytesIO()
|
262
|
+
with zipfile.ZipFile(
|
263
|
+
out_buffer, "w", zipfile.ZIP_DEFLATED, compresslevel=9
|
264
|
+
) as out_zip:
|
265
|
+
for root, _, _files in os.walk(extract_path):
|
266
|
+
for file in _files:
|
267
|
+
file_path = Path(root) / file
|
268
|
+
relative_path = file_path.relative_to(extract_path)
|
269
|
+
out_zip.write(file_path, relative_path)
|
270
|
+
|
271
|
+
return WebCompileResult(
|
272
|
+
success=True,
|
273
|
+
stdout=stdout,
|
274
|
+
hash_value=hash_value,
|
275
|
+
zip_bytes=out_buffer.getvalue(),
|
276
|
+
)
|
277
|
+
except KeyboardInterrupt:
|
278
|
+
print("Keyboard interrupt")
|
279
|
+
raise
|
280
|
+
except httpx.HTTPError as e:
|
281
|
+
print(f"Error: {e}")
|
282
|
+
return WebCompileResult(
|
283
|
+
success=False, stdout=str(e), hash_value=None, zip_bytes=b""
|
284
|
+
)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: fastled
|
3
|
-
Version: 1.0.
|
3
|
+
Version: 1.0.12
|
4
4
|
Summary: FastLED Wasm Compiler
|
5
5
|
Home-page: https://github.com/zackees/fastled-wasm
|
6
6
|
Maintainer: Zachary Vorhies
|
@@ -16,7 +16,6 @@ Requires-Dist: watchdog
|
|
16
16
|
Requires-Dist: livereload
|
17
17
|
Requires-Dist: download
|
18
18
|
Requires-Dist: filelock
|
19
|
-
Requires-Dist: windows-curses; platform_system == "Windows"
|
20
19
|
|
21
20
|
# FastLED wasm compiler
|
22
21
|
|
@@ -91,9 +90,9 @@ Pre-processing is done to your source files. A fake Arduino.h will be inserted i
|
|
91
90
|
provide shims for most of the common api points.
|
92
91
|
|
93
92
|
|
94
|
-
|
95
93
|
# Revisions
|
96
94
|
|
95
|
+
* 1.1.12 - By default, fastled will default to the web compiler. `--localhost` to either attach to an existing server launched with `--server` or else one will be created automatically and launched.
|
97
96
|
* 1.1.11 - Dev improvement: FastLED src code volume mapped into docker will just in time update without having to manually trigger it.
|
98
97
|
* 1.1.10 - Swap large assets with embedded placeholders. This helps video sketches upload and compile instantly. Assets are re-added on after compile artifacts are returned.
|
99
98
|
* 1.1.9 - Remove auto server and instead tell the user corrective action to take.
|
@@ -1,8 +1,8 @@
|
|
1
|
-
fastled/__init__.py,sha256=
|
2
|
-
fastled/app.py,sha256=
|
1
|
+
fastled/__init__.py,sha256=XO4EYoMURe1bnLz9dzABggQ31idNxZwqUDWnM5CR1oY,64
|
2
|
+
fastled/app.py,sha256=VB_uJDrFDvPgJuPh8crntWztKteWmHp5porqNSCCXu4,14949
|
3
3
|
fastled/build_mode.py,sha256=joMwsV4K1y_LijT4gEAcjx69RZBoe_KmFmHZdPYbL_4,631
|
4
4
|
fastled/cli.py,sha256=CNR_pQR0sNVPNuv8e_nmm-0PI8sU-eUBUgnWgWkzW9c,237
|
5
|
-
fastled/compile_server.py,sha256
|
5
|
+
fastled/compile_server.py,sha256=Om9fFpg9Yx4_WJDC_pG5-tYtAwreKrEAx-k8dJQBkJs,8146
|
6
6
|
fastled/docker_manager.py,sha256=gaDZpFV7E-9JhYIn6ahkP--9dGT32-WR5wZaU-o--6g,9107
|
7
7
|
fastled/filewatcher.py,sha256=fJNMQRDCpihSL4nQeYPqbD4m1Jzjcz_-YRAo-wlPW6k,6518
|
8
8
|
fastled/keyboard.py,sha256=PiAUhga8R_crMqFQRwjKyg6bgVTMlpezWa0gaGmL_So,2542
|
@@ -10,11 +10,11 @@ fastled/open_browser.py,sha256=RRHcsZ5Vzsw1AuZUEYuSfjKmf_9j3NGMDUR-FndHmqs,1483
|
|
10
10
|
fastled/paths.py,sha256=VsPmgu0lNSCFOoEC0BsTYzDygXqy15AHUfN-tTuzDZA,99
|
11
11
|
fastled/sketch.py,sha256=KhhPFqlFVlBk8YrzFy7-ioe7zEzecgrVLhyFbLpBp7k,1845
|
12
12
|
fastled/util.py,sha256=t4M3NFMhnCzfYbLvIyJi0RdFssZqbTN_vVIaej1WV-U,265
|
13
|
-
fastled/web_compile.py,sha256=
|
13
|
+
fastled/web_compile.py,sha256=RqHGiG9ZV3siQVhnQ8Ff7p-5aUKCwQWdoxs-zk7ItDE,10149
|
14
14
|
fastled/assets/example.txt,sha256=lTBovRjiz0_TgtAtbA1C5hNi2ffbqnNPqkKg6UiKCT8,54
|
15
|
-
fastled-1.0.
|
16
|
-
fastled-1.0.
|
17
|
-
fastled-1.0.
|
18
|
-
fastled-1.0.
|
19
|
-
fastled-1.0.
|
20
|
-
fastled-1.0.
|
15
|
+
fastled-1.0.12.dist-info/LICENSE,sha256=b6pOoifSXiUaz_lDS84vWlG3fr4yUKwB8fzkrH9R8bQ,1064
|
16
|
+
fastled-1.0.12.dist-info/METADATA,sha256=fpErTM2GXagl_3JPvnrw8b3SUIJ6jeTPa-QFkTilAfc,6919
|
17
|
+
fastled-1.0.12.dist-info/WHEEL,sha256=0VNUDWQJzfRahYI3neAhz2UVbRCtztpN5dPHAGvmGXc,109
|
18
|
+
fastled-1.0.12.dist-info/entry_points.txt,sha256=RCwmzCSOS4-C2i9EziANq7Z2Zb4KFnEMR1FQC0bBwAw,101
|
19
|
+
fastled-1.0.12.dist-info/top_level.txt,sha256=xfG6Z_ol9V5YmBROkZq2QTRwjbS2ouCUxaTJsOwfkOo,14
|
20
|
+
fastled-1.0.12.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|