fastled 1.4.9__tar.gz → 1.4.11__tar.gz
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-1.4.9 → fastled-1.4.11}/PKG-INFO +1 -1
- fastled-1.4.11/requirements.docker.txt +1 -0
- {fastled-1.4.9 → fastled-1.4.11}/src/fastled/__version__.py +1 -1
- fastled-1.4.11/src/fastled/interruptible_http.py +148 -0
- {fastled-1.4.9 → fastled-1.4.11}/src/fastled/open_browser.py +4 -2
- {fastled-1.4.9/src/fastled → fastled-1.4.11/src/fastled/playwright}/playwright_browser.py +2 -2
- {fastled-1.4.9 → fastled-1.4.11}/src/fastled/web_compile.py +96 -47
- {fastled-1.4.9 → fastled-1.4.11}/src/fastled.egg-info/PKG-INFO +1 -1
- {fastled-1.4.9 → fastled-1.4.11}/src/fastled.egg-info/SOURCES.txt +3 -2
- {fastled-1.4.9 → fastled-1.4.11}/tests/integration/test_playwright_integration.py +1 -1
- fastled-1.4.11/tests/unit/test_webcompile.py +81 -0
- fastled-1.4.9/requirements.docker.txt +0 -1
- fastled-1.4.9/tests/unit/test_webcompile.py +0 -43
- {fastled-1.4.9 → fastled-1.4.11}/.aiderignore +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/.cursorrules +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/.dockerignore +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/.github/workflows/build_multi_docker_image.yml +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/.github/workflows/build_webpage.yml +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/.github/workflows/lint.yml +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/.github/workflows/publish_release.yml +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/.github/workflows/template_build_docker_image.yml +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/.github/workflows/test_build_exe.yml +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/.github/workflows/test_macos.yml +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/.github/workflows/test_ubuntu.yml +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/.github/workflows/test_win.yml +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/.gitignore +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/.pylintrc +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/.vscode/launch.json +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/.vscode/settings.json +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/.vscode/tasks.json +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/DEBUGGER.md +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/Dockerfile +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/FAQ.md +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/LICENSE +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/MANIFEST.in +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/README.md +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/RELEASE.md +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/TODO.md +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/build_exe.py +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/build_local_docker.py +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/build_site.py +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/clean +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/compiler/debug.sh +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/compiler/run.py +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/demo/100dots.html +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/demo/demo_threejs.html +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/demo/micdemo.html +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/demo/mp3upload.html +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/demo/webgl_postprocessing_unreal_bloom.html +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/docker-compose.yml +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/entrypoint.sh +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/install +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/install_linux.sh +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/lint +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/pyproject.toml +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/requirements.testing.txt +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/setup.cfg +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/setup.py +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/src/fastled/__init__.py +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/src/fastled/__main__.py +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/src/fastled/app.py +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/src/fastled/args.py +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/src/fastled/assets/example.txt +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/src/fastled/assets/localhost-key.pem +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/src/fastled/assets/localhost.pem +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/src/fastled/cli.py +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/src/fastled/cli_test.py +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/src/fastled/cli_test_interactive.py +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/src/fastled/client_server.py +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/src/fastled/compile_server.py +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/src/fastled/compile_server_impl.py +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/src/fastled/docker_manager.py +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/src/fastled/filewatcher.py +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/src/fastled/find_good_connection.py +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/src/fastled/keyboard.py +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/src/fastled/keyz.py +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/src/fastled/live_client.py +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/src/fastled/parse_args.py +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/src/fastled/paths.py +0 -0
- {fastled-1.4.9/src/fastled → fastled-1.4.11/src/fastled/playwright}/chrome_extension_downloader.py +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/src/fastled/print_filter.py +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/src/fastled/project_init.py +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/src/fastled/select_sketch_directory.py +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/src/fastled/server_flask.py +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/src/fastled/server_start.py +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/src/fastled/settings.py +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/src/fastled/site/build.py +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/src/fastled/site/examples.py +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/src/fastled/sketch.py +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/src/fastled/spinner.py +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/src/fastled/string_diff.py +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/src/fastled/test/can_run_local_docker_tests.py +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/src/fastled/test/examples.py +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/src/fastled/types.py +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/src/fastled/util.py +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/src/fastled/version.py +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/src/fastled/zip_files.py +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/src/fastled.egg-info/dependency_links.txt +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/src/fastled.egg-info/entry_points.txt +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/src/fastled.egg-info/requires.txt +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/src/fastled.egg-info/top_level.txt +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/test +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/tests/integration/test_build_examples.py +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/tests/integration/test_examples.py +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/tests/integration/test_libcompile.py +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/tests/unit/html/index.html +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/tests/unit/test_api.py +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/tests/unit/test_bad_ino.py +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/tests/unit/test_banner_string.py +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/tests/unit/test_cli.py +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/tests/unit/test_cli_no_platformio.py +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/tests/unit/test_compile_server.py +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/tests/unit/test_debug_fetch_source_files.py +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/tests/unit/test_docker_linux_on_windows.py +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/tests/unit/test_embedded_data.py +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/tests/unit/test_filechanger.py +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/tests/unit/test_flask_headers.py +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/tests/unit/test_http_server.py +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/tests/unit/test_ino/bad/bad.ino +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/tests/unit/test_ino/bad_platformio/bad_platformio.ino +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/tests/unit/test_ino/bad_platformio/platformio.ini +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/tests/unit/test_ino/embedded/data/bigdata.dat +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/tests/unit/test_ino/embedded/wasm.ino +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/tests/unit/test_ino/wasm/wasm.ino +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/tests/unit/test_manual_api_invocation.py +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/tests/unit/test_no_platformio_compile.py +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/tests/unit/test_project_init.py +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/tests/unit/test_server_and_client_seperatly.py +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/tests/unit/test_session_compile.py +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/tests/unit/test_string_diff.py +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/tests/unit/test_string_diff_comprehensive.py +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/tests/unit/test_version.py +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/upload_package.sh +0 -0
- {fastled-1.4.9 → fastled-1.4.11}/vscode-plugin/readme +0 -0
@@ -0,0 +1 @@
|
|
1
|
+
fastled-wasm-server>=1.1.19
|
@@ -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.11"
|
5
5
|
|
6
6
|
__version_url_latest__ = "https://raw.githubusercontent.com/zackees/fastled-wasm/refs/heads/main/src/fastled/__version__.py"
|
@@ -0,0 +1,148 @@
|
|
1
|
+
"""
|
2
|
+
Interruptible HTTP requests that can be cancelled with Ctrl+C.
|
3
|
+
|
4
|
+
This module provides cross-platform HTTP request functionality that can be
|
5
|
+
interrupted with Ctrl+C by using asyncio cancellation and periodic checks.
|
6
|
+
"""
|
7
|
+
|
8
|
+
import asyncio
|
9
|
+
|
10
|
+
import httpx
|
11
|
+
|
12
|
+
|
13
|
+
class InterruptibleHTTPRequest:
|
14
|
+
"""A wrapper for making HTTP requests that can be interrupted by Ctrl+C."""
|
15
|
+
|
16
|
+
def __init__(self):
|
17
|
+
self.cancelled = False
|
18
|
+
|
19
|
+
async def _make_request_async(
|
20
|
+
self,
|
21
|
+
url: str,
|
22
|
+
files: dict,
|
23
|
+
headers: dict,
|
24
|
+
transport: httpx.HTTPTransport | None = None,
|
25
|
+
timeout: float = 240,
|
26
|
+
follow_redirects: bool = True,
|
27
|
+
) -> httpx.Response:
|
28
|
+
"""Make an async HTTP request."""
|
29
|
+
# Convert sync transport to async transport if provided
|
30
|
+
async_transport = None
|
31
|
+
if transport is not None:
|
32
|
+
# For IPv4 connections, create async transport with local address
|
33
|
+
async_transport = httpx.AsyncHTTPTransport(local_address="0.0.0.0")
|
34
|
+
|
35
|
+
async with httpx.AsyncClient(
|
36
|
+
transport=async_transport,
|
37
|
+
timeout=timeout,
|
38
|
+
) as client:
|
39
|
+
response = await client.post(
|
40
|
+
url,
|
41
|
+
follow_redirects=follow_redirects,
|
42
|
+
files=files,
|
43
|
+
headers=headers,
|
44
|
+
)
|
45
|
+
return response
|
46
|
+
|
47
|
+
def make_request_interruptible(
|
48
|
+
self,
|
49
|
+
url: str,
|
50
|
+
files: dict,
|
51
|
+
headers: dict,
|
52
|
+
transport: httpx.HTTPTransport | None = None,
|
53
|
+
timeout: float = 240,
|
54
|
+
follow_redirects: bool = True,
|
55
|
+
) -> httpx.Response:
|
56
|
+
"""Make an HTTP request that can be interrupted by Ctrl+C."""
|
57
|
+
try:
|
58
|
+
# Create a new event loop if we're not in one
|
59
|
+
try:
|
60
|
+
loop = asyncio.get_running_loop()
|
61
|
+
# We're already in an event loop, use run_in_executor
|
62
|
+
return asyncio.run_coroutine_threadsafe(
|
63
|
+
self._run_with_keyboard_check(
|
64
|
+
url, files, headers, transport, timeout, follow_redirects
|
65
|
+
),
|
66
|
+
loop,
|
67
|
+
).result()
|
68
|
+
except RuntimeError:
|
69
|
+
# No running loop, create one
|
70
|
+
return asyncio.run(
|
71
|
+
self._run_with_keyboard_check(
|
72
|
+
url, files, headers, transport, timeout, follow_redirects
|
73
|
+
)
|
74
|
+
)
|
75
|
+
except KeyboardInterrupt:
|
76
|
+
print("\nHTTP request cancelled by user")
|
77
|
+
raise
|
78
|
+
|
79
|
+
async def _run_with_keyboard_check(
|
80
|
+
self,
|
81
|
+
url: str,
|
82
|
+
files: dict,
|
83
|
+
headers: dict,
|
84
|
+
transport: httpx.HTTPTransport | None = None,
|
85
|
+
timeout: float = 240,
|
86
|
+
follow_redirects: bool = True,
|
87
|
+
) -> httpx.Response:
|
88
|
+
"""Run the request with periodic keyboard interrupt checks."""
|
89
|
+
task = asyncio.create_task(
|
90
|
+
self._make_request_async(
|
91
|
+
url, files, headers, transport, timeout, follow_redirects
|
92
|
+
)
|
93
|
+
)
|
94
|
+
|
95
|
+
# Poll for keyboard interrupt while waiting for the request
|
96
|
+
# This approach allows the task to be cancelled when KeyboardInterrupt
|
97
|
+
# is raised in the calling thread
|
98
|
+
while not task.done():
|
99
|
+
try:
|
100
|
+
# Wait for either completion or a short timeout
|
101
|
+
response = await asyncio.wait_for(asyncio.shield(task), timeout=0.1)
|
102
|
+
return response
|
103
|
+
except asyncio.TimeoutError:
|
104
|
+
# Continue waiting - the short timeout allows for more responsive
|
105
|
+
# cancellation when KeyboardInterrupt is raised
|
106
|
+
continue
|
107
|
+
except KeyboardInterrupt:
|
108
|
+
task.cancel()
|
109
|
+
print("\nHTTP request cancelled by user")
|
110
|
+
raise
|
111
|
+
|
112
|
+
return await task
|
113
|
+
|
114
|
+
|
115
|
+
def make_interruptible_post_request(
|
116
|
+
url: str,
|
117
|
+
files: dict | None = None,
|
118
|
+
headers: dict | None = None,
|
119
|
+
transport: httpx.HTTPTransport | None = None,
|
120
|
+
timeout: float = 240,
|
121
|
+
follow_redirects: bool = True,
|
122
|
+
) -> httpx.Response:
|
123
|
+
"""
|
124
|
+
Convenience function to make an interruptible POST request.
|
125
|
+
|
126
|
+
Args:
|
127
|
+
url: The URL to make the request to
|
128
|
+
files: Files to upload (optional)
|
129
|
+
headers: HTTP headers (optional)
|
130
|
+
transport: HTTP transport to use (optional)
|
131
|
+
timeout: Request timeout in seconds
|
132
|
+
follow_redirects: Whether to follow redirects
|
133
|
+
|
134
|
+
Returns:
|
135
|
+
The HTTP response
|
136
|
+
|
137
|
+
Raises:
|
138
|
+
KeyboardInterrupt: If the request was cancelled by Ctrl+C
|
139
|
+
"""
|
140
|
+
request_handler = InterruptibleHTTPRequest()
|
141
|
+
return request_handler.make_request_interruptible(
|
142
|
+
url=url,
|
143
|
+
files=files or {},
|
144
|
+
headers=headers or {},
|
145
|
+
transport=transport,
|
146
|
+
timeout=timeout,
|
147
|
+
follow_redirects=follow_redirects,
|
148
|
+
)
|
@@ -6,7 +6,7 @@ import weakref
|
|
6
6
|
from multiprocessing import Process
|
7
7
|
from pathlib import Path
|
8
8
|
|
9
|
-
from fastled.playwright_browser import open_with_playwright
|
9
|
+
from fastled.playwright.playwright_browser import open_with_playwright
|
10
10
|
from fastled.server_flask import run_flask_in_thread
|
11
11
|
|
12
12
|
# Global reference to keep Playwright browser alive
|
@@ -128,7 +128,9 @@ def spawn_http_server(
|
|
128
128
|
if should_use_playwright:
|
129
129
|
if app:
|
130
130
|
# For --app mode, try to install browsers if needed
|
131
|
-
from fastled.playwright_browser import
|
131
|
+
from fastled.playwright.playwright_browser import (
|
132
|
+
install_playwright_browsers,
|
133
|
+
)
|
132
134
|
|
133
135
|
install_playwright_browsers()
|
134
136
|
|
@@ -77,7 +77,7 @@ class PlaywrightBrowser:
|
|
77
77
|
def _setup_extensions(self) -> None:
|
78
78
|
"""Setup Chrome extensions for enhanced debugging."""
|
79
79
|
try:
|
80
|
-
from fastled.chrome_extension_downloader import (
|
80
|
+
from fastled.playwright.chrome_extension_downloader import (
|
81
81
|
download_cpp_devtools_extension,
|
82
82
|
)
|
83
83
|
|
@@ -706,7 +706,7 @@ def install_playwright_browsers() -> bool:
|
|
706
706
|
|
707
707
|
# Also download the C++ DevTools Support extension
|
708
708
|
try:
|
709
|
-
from fastled.chrome_extension_downloader import (
|
709
|
+
from fastled.playwright.chrome_extension_downloader import (
|
710
710
|
download_cpp_devtools_extension,
|
711
711
|
)
|
712
712
|
|
@@ -9,13 +9,14 @@ from pathlib import Path
|
|
9
9
|
import httpx
|
10
10
|
|
11
11
|
from fastled.find_good_connection import find_good_connection
|
12
|
+
from fastled.interruptible_http import make_interruptible_post_request
|
12
13
|
from fastled.settings import SERVER_PORT
|
13
14
|
from fastled.types import BuildMode, CompileResult
|
14
15
|
from fastled.zip_files import ZipResult, zip_files
|
15
16
|
|
16
17
|
DEFAULT_HOST = "https://fastled.onrender.com"
|
17
18
|
ENDPOINT_COMPILED_WASM = "compile/wasm"
|
18
|
-
_TIMEOUT = 60 * 4 #
|
19
|
+
_TIMEOUT = 60 * 4 # 4 mins timeout
|
19
20
|
_AUTH_TOKEN = "oBOT5jbsO4ztgrpNsQwlmFLIKB"
|
20
21
|
|
21
22
|
|
@@ -29,6 +30,38 @@ def _sanitize_host(host: str) -> str:
|
|
29
30
|
return host if host.startswith("http://") else f"http://{host}"
|
30
31
|
|
31
32
|
|
33
|
+
def _check_embedded_http_status(response_content: bytes) -> tuple[bool, int | None]:
|
34
|
+
"""
|
35
|
+
Check if the response content has an embedded HTTP status at the end.
|
36
|
+
|
37
|
+
Returns:
|
38
|
+
tuple: (has_embedded_status, status_code)
|
39
|
+
has_embedded_status is True if an embedded status was found
|
40
|
+
status_code is the embedded status code or None if not found
|
41
|
+
"""
|
42
|
+
try:
|
43
|
+
# Convert bytes to string for parsing
|
44
|
+
content_str = response_content.decode("utf-8", errors="ignore")
|
45
|
+
|
46
|
+
# Look for HTTP_STATUS: at the end of the content
|
47
|
+
lines = content_str.strip().split("\n")
|
48
|
+
if lines:
|
49
|
+
last_line = lines[-1].strip()
|
50
|
+
if last_line.startswith("HTTP_STATUS:"):
|
51
|
+
# Extract the status code
|
52
|
+
try:
|
53
|
+
status_code = int(last_line.split(":", 1)[1].strip())
|
54
|
+
return True, status_code
|
55
|
+
except (ValueError, IndexError):
|
56
|
+
# Malformed status line
|
57
|
+
return True, None
|
58
|
+
|
59
|
+
return False, None
|
60
|
+
except Exception:
|
61
|
+
# If we can't parse the content, assume no embedded status
|
62
|
+
return False, None
|
63
|
+
|
64
|
+
|
32
65
|
def _banner(msg: str) -> str:
|
33
66
|
"""
|
34
67
|
Create a banner for the given message.
|
@@ -83,25 +116,26 @@ def _compile_libfastled(
|
|
83
116
|
httpx.HTTPTransport(local_address="0.0.0.0") if connection_result.ipv4 else None
|
84
117
|
)
|
85
118
|
|
86
|
-
|
119
|
+
headers = {
|
120
|
+
"accept": "application/json",
|
121
|
+
"authorization": auth_token,
|
122
|
+
"build": build_mode.value.lower(),
|
123
|
+
}
|
124
|
+
|
125
|
+
url = f"{connection_result.host}/compile/libfastled"
|
126
|
+
print(f"Compiling libfastled on {url} via {ipv4_stmt}")
|
127
|
+
|
128
|
+
# Use interruptible HTTP request
|
129
|
+
response = make_interruptible_post_request(
|
130
|
+
url=url,
|
131
|
+
files={}, # No files for libfastled compilation
|
132
|
+
headers=headers,
|
87
133
|
transport=transport,
|
88
134
|
timeout=_TIMEOUT * 2, # Give more time for library compilation
|
89
|
-
|
90
|
-
|
91
|
-
"accept": "application/json",
|
92
|
-
"authorization": auth_token,
|
93
|
-
"build": build_mode.value.lower(),
|
94
|
-
}
|
95
|
-
|
96
|
-
url = f"{connection_result.host}/compile/libfastled"
|
97
|
-
print(f"Compiling libfastled on {url} via {ipv4_stmt}")
|
98
|
-
response = client.post(
|
99
|
-
url,
|
100
|
-
headers=headers,
|
101
|
-
timeout=_TIMEOUT * 2,
|
102
|
-
)
|
135
|
+
follow_redirects=False,
|
136
|
+
)
|
103
137
|
|
104
|
-
|
138
|
+
return response
|
105
139
|
|
106
140
|
|
107
141
|
def _send_compile_request(
|
@@ -131,37 +165,32 @@ def _send_compile_request(
|
|
131
165
|
|
132
166
|
archive_size = len(zip_bytes)
|
133
167
|
|
134
|
-
|
168
|
+
headers = {
|
169
|
+
"accept": "application/json",
|
170
|
+
"authorization": auth_token,
|
171
|
+
"build": (
|
172
|
+
build_mode.value.lower() if build_mode else BuildMode.QUICK.value.lower()
|
173
|
+
),
|
174
|
+
"profile": "true" if profile else "false",
|
175
|
+
"no-platformio": "true" if no_platformio else "false",
|
176
|
+
"allow-libcompile": "false", # Always false since we handle it manually
|
177
|
+
}
|
178
|
+
|
179
|
+
url = f"{connection_result.host}/{ENDPOINT_COMPILED_WASM}"
|
180
|
+
print(f"Compiling sketch on {url} via {ipv4_stmt}. Zip size: {archive_size} bytes")
|
181
|
+
files = {"file": ("wasm.zip", zip_bytes, "application/x-zip-compressed")}
|
182
|
+
|
183
|
+
# Use interruptible HTTP request
|
184
|
+
response = make_interruptible_post_request(
|
185
|
+
url=url,
|
186
|
+
files=files,
|
187
|
+
headers=headers,
|
135
188
|
transport=transport,
|
136
189
|
timeout=_TIMEOUT,
|
137
|
-
|
138
|
-
|
139
|
-
"accept": "application/json",
|
140
|
-
"authorization": auth_token,
|
141
|
-
"build": (
|
142
|
-
build_mode.value.lower()
|
143
|
-
if build_mode
|
144
|
-
else BuildMode.QUICK.value.lower()
|
145
|
-
),
|
146
|
-
"profile": "true" if profile else "false",
|
147
|
-
"no-platformio": "true" if no_platformio else "false",
|
148
|
-
"allow-libcompile": "false", # Always false since we handle it manually
|
149
|
-
}
|
150
|
-
|
151
|
-
url = f"{connection_result.host}/{ENDPOINT_COMPILED_WASM}"
|
152
|
-
print(
|
153
|
-
f"Compiling sketch on {url} via {ipv4_stmt}. Zip size: {archive_size} bytes"
|
154
|
-
)
|
155
|
-
files = {"file": ("wasm.zip", zip_bytes, "application/x-zip-compressed")}
|
156
|
-
response = client.post(
|
157
|
-
url,
|
158
|
-
follow_redirects=True,
|
159
|
-
files=files,
|
160
|
-
headers=headers,
|
161
|
-
timeout=_TIMEOUT,
|
162
|
-
)
|
190
|
+
follow_redirects=True,
|
191
|
+
)
|
163
192
|
|
164
|
-
|
193
|
+
return response
|
165
194
|
|
166
195
|
|
167
196
|
def _process_compile_response(
|
@@ -264,13 +293,33 @@ def web_compile(
|
|
264
293
|
print("Step 1: Compiling libfastled...")
|
265
294
|
try:
|
266
295
|
libfastled_response = _compile_libfastled(host, auth_token, build_mode)
|
296
|
+
|
297
|
+
# Check HTTP response status first
|
267
298
|
if libfastled_response.status_code != 200:
|
268
299
|
print(
|
269
|
-
f"Warning: libfastled compilation failed with status {libfastled_response.status_code}"
|
300
|
+
f"Warning: libfastled compilation failed with HTTP status {libfastled_response.status_code}"
|
270
301
|
)
|
271
302
|
# Continue with sketch compilation even if libfastled fails
|
272
303
|
else:
|
273
|
-
|
304
|
+
# Check for embedded HTTP status in response content
|
305
|
+
has_embedded_status, embedded_status = _check_embedded_http_status(
|
306
|
+
libfastled_response.content
|
307
|
+
)
|
308
|
+
if has_embedded_status:
|
309
|
+
if embedded_status is not None and embedded_status != 200:
|
310
|
+
print(
|
311
|
+
f"Warning: libfastled compilation failed with embedded status {embedded_status}"
|
312
|
+
)
|
313
|
+
# Continue with sketch compilation even if libfastled fails
|
314
|
+
elif embedded_status is None:
|
315
|
+
print(
|
316
|
+
"Warning: libfastled compilation returned malformed embedded status"
|
317
|
+
)
|
318
|
+
# Continue with sketch compilation even if libfastled fails
|
319
|
+
else:
|
320
|
+
print("✅ libfastled compilation successful")
|
321
|
+
else:
|
322
|
+
print("✅ libfastled compilation successful")
|
274
323
|
except Exception as e:
|
275
324
|
print(f"Warning: libfastled compilation failed: {e}")
|
276
325
|
# Continue with sketch compilation even if libfastled fails
|
@@ -50,7 +50,6 @@ src/fastled/__main__.py
|
|
50
50
|
src/fastled/__version__.py
|
51
51
|
src/fastled/app.py
|
52
52
|
src/fastled/args.py
|
53
|
-
src/fastled/chrome_extension_downloader.py
|
54
53
|
src/fastled/cli.py
|
55
54
|
src/fastled/cli_test.py
|
56
55
|
src/fastled/cli_test_interactive.py
|
@@ -60,13 +59,13 @@ src/fastled/compile_server_impl.py
|
|
60
59
|
src/fastled/docker_manager.py
|
61
60
|
src/fastled/filewatcher.py
|
62
61
|
src/fastled/find_good_connection.py
|
62
|
+
src/fastled/interruptible_http.py
|
63
63
|
src/fastled/keyboard.py
|
64
64
|
src/fastled/keyz.py
|
65
65
|
src/fastled/live_client.py
|
66
66
|
src/fastled/open_browser.py
|
67
67
|
src/fastled/parse_args.py
|
68
68
|
src/fastled/paths.py
|
69
|
-
src/fastled/playwright_browser.py
|
70
69
|
src/fastled/print_filter.py
|
71
70
|
src/fastled/project_init.py
|
72
71
|
src/fastled/select_sketch_directory.py
|
@@ -90,6 +89,8 @@ src/fastled.egg-info/top_level.txt
|
|
90
89
|
src/fastled/assets/example.txt
|
91
90
|
src/fastled/assets/localhost-key.pem
|
92
91
|
src/fastled/assets/localhost.pem
|
92
|
+
src/fastled/playwright/chrome_extension_downloader.py
|
93
|
+
src/fastled/playwright/playwright_browser.py
|
93
94
|
src/fastled/site/build.py
|
94
95
|
src/fastled/site/examples.py
|
95
96
|
src/fastled/test/can_run_local_docker_tests.py
|
@@ -5,7 +5,7 @@ Unit tests for Playwright integration.
|
|
5
5
|
import unittest
|
6
6
|
from unittest.mock import MagicMock, patch
|
7
7
|
|
8
|
-
from fastled.playwright_browser import open_with_playwright
|
8
|
+
from fastled.playwright.playwright_browser import open_with_playwright
|
9
9
|
|
10
10
|
|
11
11
|
class PlaywrightIntegrationTester(unittest.TestCase):
|
@@ -0,0 +1,81 @@
|
|
1
|
+
import time
|
2
|
+
import unittest
|
3
|
+
from pathlib import Path
|
4
|
+
|
5
|
+
from fastled.web_compile import _check_embedded_http_status, web_compile
|
6
|
+
|
7
|
+
HERE = Path(__file__).parent
|
8
|
+
TEST_DIR = HERE / "test_ino" / "wasm"
|
9
|
+
|
10
|
+
_USE_LOCALHOST = False
|
11
|
+
_HOST = "http://localhost" if _USE_LOCALHOST else None
|
12
|
+
|
13
|
+
|
14
|
+
class WebCompileTester(unittest.TestCase):
|
15
|
+
"""Main tester class."""
|
16
|
+
|
17
|
+
def test_compile(self) -> None:
|
18
|
+
"""Test web compilation functionality with real server."""
|
19
|
+
# Test the web_compile function with actual server call
|
20
|
+
start = time.time()
|
21
|
+
result = web_compile(TEST_DIR, host=_HOST)
|
22
|
+
diff = time.time() - start
|
23
|
+
print(f"Time taken: {diff:.2f} seconds")
|
24
|
+
|
25
|
+
# Verify we got a successful result
|
26
|
+
self.assertTrue(result.success, f"Compilation failed: {result.stdout}")
|
27
|
+
|
28
|
+
# Verify we got actual WASM data back
|
29
|
+
self.assertTrue(len(result.zip_bytes) > 0)
|
30
|
+
|
31
|
+
# Print compilation output for debugging
|
32
|
+
print(f"Compilation stdout:\n{result.stdout}")
|
33
|
+
|
34
|
+
print(f"Zip size: {len(result.zip_bytes)} bytes")
|
35
|
+
|
36
|
+
def test_invalid_directory(self) -> None:
|
37
|
+
"""Test handling of invalid directory."""
|
38
|
+
with self.assertRaises(FileNotFoundError):
|
39
|
+
web_compile(Path("nonexistent_directory"))
|
40
|
+
|
41
|
+
def test_embedded_http_status_parsing(self) -> None:
|
42
|
+
"""Test the _check_embedded_http_status function."""
|
43
|
+
# Test successful case (no embedded status)
|
44
|
+
content_success = b"Some output\nCompilation successful\n"
|
45
|
+
has_status, status_code = _check_embedded_http_status(content_success)
|
46
|
+
self.assertFalse(has_status)
|
47
|
+
self.assertIsNone(status_code)
|
48
|
+
|
49
|
+
# Test embedded 400 status
|
50
|
+
content_400 = b"Some error output\nHTTP_STATUS: 400"
|
51
|
+
has_status, status_code = _check_embedded_http_status(content_400)
|
52
|
+
self.assertTrue(has_status)
|
53
|
+
self.assertEqual(status_code, 400)
|
54
|
+
|
55
|
+
# Test embedded 200 status
|
56
|
+
content_200 = b"Some output\nHTTP_STATUS: 200"
|
57
|
+
has_status, status_code = _check_embedded_http_status(content_200)
|
58
|
+
self.assertTrue(has_status)
|
59
|
+
self.assertEqual(status_code, 200)
|
60
|
+
|
61
|
+
# Test malformed embedded status
|
62
|
+
content_malformed = b"Some output\nHTTP_STATUS: invalid"
|
63
|
+
has_status, status_code = _check_embedded_http_status(content_malformed)
|
64
|
+
self.assertTrue(has_status)
|
65
|
+
self.assertIsNone(status_code)
|
66
|
+
|
67
|
+
# Test empty content
|
68
|
+
content_empty = b""
|
69
|
+
has_status, status_code = _check_embedded_http_status(content_empty)
|
70
|
+
self.assertFalse(has_status)
|
71
|
+
self.assertIsNone(status_code)
|
72
|
+
|
73
|
+
# Test content with embedded status in middle (should not be detected)
|
74
|
+
content_middle = b"Some output\nHTTP_STATUS: 400\nMore output"
|
75
|
+
has_status, status_code = _check_embedded_http_status(content_middle)
|
76
|
+
self.assertFalse(has_status)
|
77
|
+
self.assertIsNone(status_code)
|
78
|
+
|
79
|
+
|
80
|
+
if __name__ == "__main__":
|
81
|
+
unittest.main()
|
@@ -1 +0,0 @@
|
|
1
|
-
fastled-wasm-server>=1.1.18
|
@@ -1,43 +0,0 @@
|
|
1
|
-
import time
|
2
|
-
import unittest
|
3
|
-
from pathlib import Path
|
4
|
-
|
5
|
-
from fastled.web_compile import web_compile
|
6
|
-
|
7
|
-
HERE = Path(__file__).parent
|
8
|
-
TEST_DIR = HERE / "test_ino" / "wasm"
|
9
|
-
|
10
|
-
_USE_LOCALHOST = False
|
11
|
-
_HOST = "http://localhost" if _USE_LOCALHOST else None
|
12
|
-
|
13
|
-
|
14
|
-
class WebCompileTester(unittest.TestCase):
|
15
|
-
"""Main tester class."""
|
16
|
-
|
17
|
-
def test_compile(self) -> None:
|
18
|
-
"""Test web compilation functionality with real server."""
|
19
|
-
# Test the web_compile function with actual server call
|
20
|
-
start = time.time()
|
21
|
-
result = web_compile(TEST_DIR, host=_HOST)
|
22
|
-
diff = time.time() - start
|
23
|
-
print(f"Time taken: {diff:.2f} seconds")
|
24
|
-
|
25
|
-
# Verify we got a successful result
|
26
|
-
self.assertTrue(result.success, f"Compilation failed: {result.stdout}")
|
27
|
-
|
28
|
-
# Verify we got actual WASM data back
|
29
|
-
self.assertTrue(len(result.zip_bytes) > 0)
|
30
|
-
|
31
|
-
# Print compilation output for debugging
|
32
|
-
print(f"Compilation stdout:\n{result.stdout}")
|
33
|
-
|
34
|
-
print(f"Zip size: {len(result.zip_bytes)} bytes")
|
35
|
-
|
36
|
-
def test_invalid_directory(self) -> None:
|
37
|
-
"""Test handling of invalid directory."""
|
38
|
-
with self.assertRaises(FileNotFoundError):
|
39
|
-
web_compile(Path("nonexistent_directory"))
|
40
|
-
|
41
|
-
|
42
|
-
if __name__ == "__main__":
|
43
|
-
unittest.main()
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{fastled-1.4.9/src/fastled → fastled-1.4.11/src/fastled/playwright}/chrome_extension_downloader.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|