fastled 1.4.36__py3-none-any.whl → 1.4.38__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 +1 -1
- fastled/compile_server.py +6 -0
- fastled/compile_server_impl.py +11 -0
- fastled/header_dump.py +63 -0
- fastled/live_client.py +12 -0
- fastled/parse_args.py +9 -0
- fastled/print_filter.py +52 -52
- fastled/util.py +34 -0
- fastled/version.py +41 -41
- {fastled-1.4.36.dist-info → fastled-1.4.38.dist-info}/METADATA +532 -532
- {fastled-1.4.36.dist-info → fastled-1.4.38.dist-info}/RECORD +15 -14
- {fastled-1.4.36.dist-info → fastled-1.4.38.dist-info}/WHEEL +0 -0
- {fastled-1.4.36.dist-info → fastled-1.4.38.dist-info}/entry_points.txt +0 -0
- {fastled-1.4.36.dist-info → fastled-1.4.38.dist-info}/licenses/LICENSE +0 -0
- {fastled-1.4.36.dist-info → fastled-1.4.38.dist-info}/top_level.txt +0 -0
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.4.
|
4
|
+
__version__ = "1.4.38"
|
5
5
|
|
6
6
|
__version_url_latest__ = "https://raw.githubusercontent.com/zackees/fastled-wasm/refs/heads/main/src/fastled/__version__.py"
|
fastled/compile_server.py
CHANGED
@@ -101,3 +101,9 @@ class CompileServer:
|
|
101
101
|
|
102
102
|
def process_running(self) -> bool:
|
103
103
|
return self.impl.process_running()
|
104
|
+
|
105
|
+
def get_emsdk_headers(self, filepath: Path) -> None:
|
106
|
+
"""Get EMSDK headers ZIP data from the server and save to filepath."""
|
107
|
+
if not str(filepath).endswith(".zip"):
|
108
|
+
raise ValueError("Filepath must end with .zip")
|
109
|
+
return self.impl.get_emsdk_headers(filepath)
|
fastled/compile_server_impl.py
CHANGED
@@ -338,3 +338,14 @@ class CompileServerImpl:
|
|
338
338
|
self.docker.suspend_container(self.container_name)
|
339
339
|
self._port = 0
|
340
340
|
print("Compile server stopped")
|
341
|
+
|
342
|
+
def get_emsdk_headers(self, filepath: Path) -> None:
|
343
|
+
"""Get EMSDK headers ZIP data from the server and save to filepath."""
|
344
|
+
from fastled.util import download_emsdk_headers
|
345
|
+
|
346
|
+
if not self._port:
|
347
|
+
raise RuntimeError("Server has not been started yet")
|
348
|
+
if not self.ping():
|
349
|
+
raise RuntimeError("Server is not running")
|
350
|
+
|
351
|
+
download_emsdk_headers(self.url(), filepath)
|
fastled/header_dump.py
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
"""Header dump functionality for EMSDK headers export."""
|
2
|
+
|
3
|
+
import sys
|
4
|
+
from pathlib import Path
|
5
|
+
|
6
|
+
|
7
|
+
def dump_emsdk_headers(output_path: Path | str, server_url: str | None = None) -> None:
|
8
|
+
"""
|
9
|
+
Dump EMSDK headers to a specified path.
|
10
|
+
|
11
|
+
Args:
|
12
|
+
output_path: Path where to save the headers ZIP file
|
13
|
+
server_url: URL of the server. If None, tries to create local server first,
|
14
|
+
then falls back to remote server if local fails.
|
15
|
+
"""
|
16
|
+
from fastled import Api
|
17
|
+
from fastled.settings import DEFAULT_URL
|
18
|
+
from fastled.util import download_emsdk_headers
|
19
|
+
|
20
|
+
# Convert to Path if string
|
21
|
+
if isinstance(output_path, str):
|
22
|
+
output_path = Path(output_path)
|
23
|
+
|
24
|
+
ends_with_zip = output_path.suffix == ".zip"
|
25
|
+
if not ends_with_zip:
|
26
|
+
raise ValueError(f"{output_path} must end with .zip")
|
27
|
+
|
28
|
+
try:
|
29
|
+
if server_url is not None:
|
30
|
+
# Use the provided server URL
|
31
|
+
download_emsdk_headers(server_url, output_path)
|
32
|
+
print(f"SUCCESS: EMSDK headers exported to {output_path}")
|
33
|
+
else:
|
34
|
+
# Try to create local server first
|
35
|
+
try:
|
36
|
+
with Api.server() as server:
|
37
|
+
base_url = server.url()
|
38
|
+
download_emsdk_headers(base_url, output_path)
|
39
|
+
print(
|
40
|
+
f"SUCCESS: EMSDK headers exported to {output_path} (using local server)"
|
41
|
+
)
|
42
|
+
except Exception as local_error:
|
43
|
+
print(
|
44
|
+
f"WARNING: Local server failed ({local_error}), falling back to remote server"
|
45
|
+
)
|
46
|
+
# Fall back to remote server
|
47
|
+
download_emsdk_headers(DEFAULT_URL, output_path)
|
48
|
+
print(
|
49
|
+
f"SUCCESS: EMSDK headers exported to {output_path} (using remote server)"
|
50
|
+
)
|
51
|
+
|
52
|
+
except Exception as e:
|
53
|
+
print(f"ERROR: Exception during header dump: {e}")
|
54
|
+
sys.exit(1)
|
55
|
+
|
56
|
+
|
57
|
+
if __name__ == "__main__":
|
58
|
+
if len(sys.argv) != 2:
|
59
|
+
print("Usage: python -m fastled.header_dump <output_path>")
|
60
|
+
sys.exit(1)
|
61
|
+
|
62
|
+
output_path = sys.argv[1]
|
63
|
+
dump_emsdk_headers(output_path)
|
fastled/live_client.py
CHANGED
@@ -99,3 +99,15 @@ class LiveClient:
|
|
99
99
|
|
100
100
|
def __exit__(self, exc_type, exc_value, traceback) -> None:
|
101
101
|
self.finalize()
|
102
|
+
|
103
|
+
def get_emsdk_headers(self, filepath: Path) -> None:
|
104
|
+
"""Get EMSDK headers ZIP data from the server and save to filepath."""
|
105
|
+
if isinstance(self.host, CompileServer):
|
106
|
+
self.host.get_emsdk_headers(filepath)
|
107
|
+
else:
|
108
|
+
# Handle string host or None case by using web_compile approach
|
109
|
+
from fastled.settings import DEFAULT_URL
|
110
|
+
from fastled.util import download_emsdk_headers
|
111
|
+
|
112
|
+
base_url = self.host if isinstance(self.host, str) else DEFAULT_URL
|
113
|
+
download_emsdk_headers(base_url, filepath)
|
fastled/parse_args.py
CHANGED
@@ -36,6 +36,7 @@ FastLED WASM Compiler - Useful options:
|
|
36
36
|
--update Update the docker image for the wasm compiler
|
37
37
|
--background-update Update the docker image in the background after compilation
|
38
38
|
--purge Remove all FastLED containers and images
|
39
|
+
--emsdk-headers <path> Export EMSDK headers ZIP to specified path
|
39
40
|
--version Show version information
|
40
41
|
--help Show detailed help
|
41
42
|
Examples:
|
@@ -209,6 +210,14 @@ def parse_args() -> Args:
|
|
209
210
|
|
210
211
|
args = parser.parse_args()
|
211
212
|
|
213
|
+
# Handle --emsdk-headers early before other processing
|
214
|
+
if args.emsdk_headers:
|
215
|
+
from fastled.header_dump import dump_emsdk_headers
|
216
|
+
|
217
|
+
out_path = args.emsdk_headers
|
218
|
+
dump_emsdk_headers(out_path)
|
219
|
+
sys.exit(0)
|
220
|
+
|
212
221
|
# Auto-enable app mode if debug is used and Playwright cache exists
|
213
222
|
if args.debug and not args.app:
|
214
223
|
playwright_dir = Path.home() / ".fastled" / "playwright"
|
fastled/print_filter.py
CHANGED
@@ -1,52 +1,52 @@
|
|
1
|
-
import re
|
2
|
-
from abc import ABC, abstractmethod
|
3
|
-
from enum import Enum
|
4
|
-
|
5
|
-
|
6
|
-
class PrintFilter(ABC):
|
7
|
-
"""Abstract base class for filtering text output."""
|
8
|
-
|
9
|
-
def __init__(self, echo: bool = True) -> None:
|
10
|
-
self.echo = echo
|
11
|
-
|
12
|
-
@abstractmethod
|
13
|
-
def filter(self, text: str) -> str:
|
14
|
-
"""Filter the text according to implementation-specific rules."""
|
15
|
-
pass
|
16
|
-
|
17
|
-
def print(self, text: str | bytes) -> str:
|
18
|
-
"""Prints the text to the console after filtering."""
|
19
|
-
if isinstance(text, bytes):
|
20
|
-
text = text.decode("utf-8")
|
21
|
-
text = self.filter(text)
|
22
|
-
if self.echo:
|
23
|
-
print(text, end="")
|
24
|
-
return text
|
25
|
-
|
26
|
-
|
27
|
-
def _handle_ino_cpp(line: str) -> str:
|
28
|
-
if ".ino.cpp" in line[0:30]:
|
29
|
-
# Extract the filename without path and extension
|
30
|
-
match = re.search(r"src/([^/]+)\.ino\.cpp", line)
|
31
|
-
if match:
|
32
|
-
filename = match.group(1)
|
33
|
-
# Replace with examples/Filename/Filename.ino format
|
34
|
-
line = line.replace(
|
35
|
-
f"src/{filename}.ino.cpp", f"examples/{filename}/{filename}.ino"
|
36
|
-
)
|
37
|
-
else:
|
38
|
-
# Fall back to simple extension replacement if regex doesn't match
|
39
|
-
line = line.replace(".ino.cpp", ".ino")
|
40
|
-
return line
|
41
|
-
|
42
|
-
|
43
|
-
class PrintFilterDefault(PrintFilter):
|
44
|
-
"""Provides default filtering for FastLED output."""
|
45
|
-
|
46
|
-
def filter(self, text: str) -> str:
|
47
|
-
return text
|
48
|
-
|
49
|
-
|
50
|
-
class CompileOrLink(Enum):
|
51
|
-
COMPILE = "compile"
|
52
|
-
LINK = "link"
|
1
|
+
import re
|
2
|
+
from abc import ABC, abstractmethod
|
3
|
+
from enum import Enum
|
4
|
+
|
5
|
+
|
6
|
+
class PrintFilter(ABC):
|
7
|
+
"""Abstract base class for filtering text output."""
|
8
|
+
|
9
|
+
def __init__(self, echo: bool = True) -> None:
|
10
|
+
self.echo = echo
|
11
|
+
|
12
|
+
@abstractmethod
|
13
|
+
def filter(self, text: str) -> str:
|
14
|
+
"""Filter the text according to implementation-specific rules."""
|
15
|
+
pass
|
16
|
+
|
17
|
+
def print(self, text: str | bytes) -> str:
|
18
|
+
"""Prints the text to the console after filtering."""
|
19
|
+
if isinstance(text, bytes):
|
20
|
+
text = text.decode("utf-8")
|
21
|
+
text = self.filter(text)
|
22
|
+
if self.echo:
|
23
|
+
print(text, end="")
|
24
|
+
return text
|
25
|
+
|
26
|
+
|
27
|
+
def _handle_ino_cpp(line: str) -> str:
|
28
|
+
if ".ino.cpp" in line[0:30]:
|
29
|
+
# Extract the filename without path and extension
|
30
|
+
match = re.search(r"src/([^/]+)\.ino\.cpp", line)
|
31
|
+
if match:
|
32
|
+
filename = match.group(1)
|
33
|
+
# Replace with examples/Filename/Filename.ino format
|
34
|
+
line = line.replace(
|
35
|
+
f"src/{filename}.ino.cpp", f"examples/{filename}/{filename}.ino"
|
36
|
+
)
|
37
|
+
else:
|
38
|
+
# Fall back to simple extension replacement if regex doesn't match
|
39
|
+
line = line.replace(".ino.cpp", ".ino")
|
40
|
+
return line
|
41
|
+
|
42
|
+
|
43
|
+
class PrintFilterDefault(PrintFilter):
|
44
|
+
"""Provides default filtering for FastLED output."""
|
45
|
+
|
46
|
+
def filter(self, text: str) -> str:
|
47
|
+
return text
|
48
|
+
|
49
|
+
|
50
|
+
class CompileOrLink(Enum):
|
51
|
+
COMPILE = "compile"
|
52
|
+
LINK = "link"
|
fastled/util.py
CHANGED
@@ -52,3 +52,37 @@ def find_free_port(start_port: int, end_port: int) -> int | None:
|
|
52
52
|
f"No free port found in the range {start_port}-{end_port}. Using {start_port}."
|
53
53
|
)
|
54
54
|
return None
|
55
|
+
|
56
|
+
|
57
|
+
def download_emsdk_headers(base_url: str, filepath: Path) -> None:
|
58
|
+
"""Download EMSDK headers from the specified URL and save to filepath.
|
59
|
+
|
60
|
+
Args:
|
61
|
+
base_url: Base URL of the server (e.g., 'http://localhost:8080')
|
62
|
+
filepath: Path where to save the headers ZIP file (must end with .zip)
|
63
|
+
|
64
|
+
Raises:
|
65
|
+
ValueError: If filepath doesn't end with .zip
|
66
|
+
RuntimeError: If download fails or server returns error
|
67
|
+
"""
|
68
|
+
if not str(filepath).endswith(".zip"):
|
69
|
+
raise ValueError("Filepath must end with .zip")
|
70
|
+
|
71
|
+
import httpx
|
72
|
+
|
73
|
+
try:
|
74
|
+
timeout = httpx.Timeout(30.0, read=30.0)
|
75
|
+
with httpx.stream(
|
76
|
+
"GET", f"{base_url}/headers/emsdk", timeout=timeout
|
77
|
+
) as response:
|
78
|
+
if response.status_code == 200:
|
79
|
+
filepath.parent.mkdir(parents=True, exist_ok=True)
|
80
|
+
with open(filepath, "wb") as f:
|
81
|
+
for chunk in response.iter_bytes(chunk_size=512000):
|
82
|
+
f.write(chunk)
|
83
|
+
else:
|
84
|
+
raise RuntimeError(
|
85
|
+
f"Failed to get EMSDK headers: HTTP {response.status_code}"
|
86
|
+
)
|
87
|
+
except Exception as e:
|
88
|
+
raise RuntimeError(f"Error downloading EMSDK headers: {e}") from e
|
fastled/version.py
CHANGED
@@ -1,41 +1,41 @@
|
|
1
|
-
from concurrent.futures import Future, ThreadPoolExecutor
|
2
|
-
|
3
|
-
import httpx
|
4
|
-
|
5
|
-
from fastled.__version__ import __version_url_latest__
|
6
|
-
|
7
|
-
|
8
|
-
def _fetch_version() -> str | Exception:
|
9
|
-
"""
|
10
|
-
Helper function to fetch the latest version from the GitHub repository.
|
11
|
-
"""
|
12
|
-
try:
|
13
|
-
response = httpx.get(__version_url_latest__)
|
14
|
-
response.raise_for_status()
|
15
|
-
# Extract the version string from the response text
|
16
|
-
version_line = response.text.split("__version__ = ")[1].split('"')[1]
|
17
|
-
return version_line
|
18
|
-
except Exception as e:
|
19
|
-
return e
|
20
|
-
|
21
|
-
|
22
|
-
def get_latest_version() -> Future[str | Exception]:
|
23
|
-
"""
|
24
|
-
Fetch the latest version from the GitHub repository.
|
25
|
-
Returns a future that will resolve with the version string or an exception.
|
26
|
-
"""
|
27
|
-
executor = ThreadPoolExecutor()
|
28
|
-
return executor.submit(_fetch_version)
|
29
|
-
|
30
|
-
|
31
|
-
def unit_test() -> None:
|
32
|
-
future = get_latest_version()
|
33
|
-
latest_version = future.result() # Wait for the future to complete
|
34
|
-
if isinstance(latest_version, Exception):
|
35
|
-
print(f"Error fetching latest version: {latest_version}")
|
36
|
-
else:
|
37
|
-
print(f"Latest version: {latest_version}")
|
38
|
-
|
39
|
-
|
40
|
-
if __name__ == "__main__":
|
41
|
-
unit_test()
|
1
|
+
from concurrent.futures import Future, ThreadPoolExecutor
|
2
|
+
|
3
|
+
import httpx
|
4
|
+
|
5
|
+
from fastled.__version__ import __version_url_latest__
|
6
|
+
|
7
|
+
|
8
|
+
def _fetch_version() -> str | Exception:
|
9
|
+
"""
|
10
|
+
Helper function to fetch the latest version from the GitHub repository.
|
11
|
+
"""
|
12
|
+
try:
|
13
|
+
response = httpx.get(__version_url_latest__)
|
14
|
+
response.raise_for_status()
|
15
|
+
# Extract the version string from the response text
|
16
|
+
version_line = response.text.split("__version__ = ")[1].split('"')[1]
|
17
|
+
return version_line
|
18
|
+
except Exception as e:
|
19
|
+
return e
|
20
|
+
|
21
|
+
|
22
|
+
def get_latest_version() -> Future[str | Exception]:
|
23
|
+
"""
|
24
|
+
Fetch the latest version from the GitHub repository.
|
25
|
+
Returns a future that will resolve with the version string or an exception.
|
26
|
+
"""
|
27
|
+
executor = ThreadPoolExecutor()
|
28
|
+
return executor.submit(_fetch_version)
|
29
|
+
|
30
|
+
|
31
|
+
def unit_test() -> None:
|
32
|
+
future = get_latest_version()
|
33
|
+
latest_version = future.result() # Wait for the future to complete
|
34
|
+
if isinstance(latest_version, Exception):
|
35
|
+
print(f"Error fetching latest version: {latest_version}")
|
36
|
+
else:
|
37
|
+
print(f"Latest version: {latest_version}")
|
38
|
+
|
39
|
+
|
40
|
+
if __name__ == "__main__":
|
41
|
+
unit_test()
|