fastled 1.3.33__tar.gz → 1.3.34__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.3.33 → fastled-1.3.34}/PKG-INFO +1 -1
- {fastled-1.3.33 → fastled-1.3.34}/src/fastled/__init__.py +6 -0
- {fastled-1.3.33 → fastled-1.3.34}/src/fastled/__version__.py +1 -1
- {fastled-1.3.33 → fastled-1.3.34}/src/fastled/app.py +5 -1
- {fastled-1.3.33 → fastled-1.3.34}/src/fastled/client_server.py +21 -6
- {fastled-1.3.33 → fastled-1.3.34}/src/fastled/compile_server.py +2 -0
- {fastled-1.3.33 → fastled-1.3.34}/src/fastled/compile_server_impl.py +4 -0
- {fastled-1.3.33 → fastled-1.3.34}/src/fastled/live_client.py +3 -0
- {fastled-1.3.33 → fastled-1.3.34}/src/fastled/server_flask.py +8 -0
- {fastled-1.3.33 → fastled-1.3.34}/src/fastled.egg-info/PKG-INFO +1 -1
- {fastled-1.3.33 → fastled-1.3.34}/src/fastled.egg-info/SOURCES.txt +1 -0
- fastled-1.3.34/tests/unit/test_flask_headers.py +188 -0
- {fastled-1.3.33 → fastled-1.3.34}/tests/unit/test_no_platformio_compile.py +196 -0
- {fastled-1.3.33 → fastled-1.3.34}/.aiderignore +0 -0
- {fastled-1.3.33 → fastled-1.3.34}/.cursorrules +0 -0
- {fastled-1.3.33 → fastled-1.3.34}/.dockerignore +0 -0
- {fastled-1.3.33 → fastled-1.3.34}/.github/workflows/build_multi_docker_image.yml +0 -0
- {fastled-1.3.33 → fastled-1.3.34}/.github/workflows/build_webpage.yml +0 -0
- {fastled-1.3.33 → fastled-1.3.34}/.github/workflows/lint.yml +0 -0
- {fastled-1.3.33 → fastled-1.3.34}/.github/workflows/publish_release.yml +0 -0
- {fastled-1.3.33 → fastled-1.3.34}/.github/workflows/template_build_docker_image.yml +0 -0
- {fastled-1.3.33 → fastled-1.3.34}/.github/workflows/test_build_exe.yml +0 -0
- {fastled-1.3.33 → fastled-1.3.34}/.github/workflows/test_macos.yml +0 -0
- {fastled-1.3.33 → fastled-1.3.34}/.github/workflows/test_ubuntu.yml +0 -0
- {fastled-1.3.33 → fastled-1.3.34}/.github/workflows/test_win.yml +0 -0
- {fastled-1.3.33 → fastled-1.3.34}/.gitignore +0 -0
- {fastled-1.3.33 → fastled-1.3.34}/.pylintrc +0 -0
- {fastled-1.3.33 → fastled-1.3.34}/.vscode/launch.json +0 -0
- {fastled-1.3.33 → fastled-1.3.34}/.vscode/settings.json +0 -0
- {fastled-1.3.33 → fastled-1.3.34}/.vscode/tasks.json +0 -0
- {fastled-1.3.33 → fastled-1.3.34}/DEBUGGER.md +0 -0
- {fastled-1.3.33 → fastled-1.3.34}/Dockerfile +0 -0
- {fastled-1.3.33 → fastled-1.3.34}/FAQ.md +0 -0
- {fastled-1.3.33 → fastled-1.3.34}/LICENSE +0 -0
- {fastled-1.3.33 → fastled-1.3.34}/MANIFEST.in +0 -0
- {fastled-1.3.33 → fastled-1.3.34}/README.md +0 -0
- {fastled-1.3.33 → fastled-1.3.34}/RELEASE.md +0 -0
- {fastled-1.3.33 → fastled-1.3.34}/TODO.md +0 -0
- {fastled-1.3.33 → fastled-1.3.34}/build_exe.py +0 -0
- {fastled-1.3.33 → fastled-1.3.34}/build_local_docker.py +0 -0
- {fastled-1.3.33 → fastled-1.3.34}/build_site.py +0 -0
- {fastled-1.3.33 → fastled-1.3.34}/clean +0 -0
- {fastled-1.3.33 → fastled-1.3.34}/compiler/debug.sh +0 -0
- {fastled-1.3.33 → fastled-1.3.34}/compiler/run.py +0 -0
- {fastled-1.3.33 → fastled-1.3.34}/demo/100dots.html +0 -0
- {fastled-1.3.33 → fastled-1.3.34}/demo/demo_threejs.html +0 -0
- {fastled-1.3.33 → fastled-1.3.34}/demo/micdemo.html +0 -0
- {fastled-1.3.33 → fastled-1.3.34}/demo/mp3upload.html +0 -0
- {fastled-1.3.33 → fastled-1.3.34}/demo/webgl_postprocessing_unreal_bloom.html +0 -0
- {fastled-1.3.33 → fastled-1.3.34}/docker-compose.yml +0 -0
- {fastled-1.3.33 → fastled-1.3.34}/entrypoint.sh +0 -0
- {fastled-1.3.33 → fastled-1.3.34}/install +0 -0
- {fastled-1.3.33 → fastled-1.3.34}/install_linux.sh +0 -0
- {fastled-1.3.33 → fastled-1.3.34}/lint +0 -0
- {fastled-1.3.33 → fastled-1.3.34}/pyproject.toml +0 -0
- {fastled-1.3.33 → fastled-1.3.34}/requirements.docker.txt +0 -0
- {fastled-1.3.33 → fastled-1.3.34}/requirements.testing.txt +0 -0
- {fastled-1.3.33 → fastled-1.3.34}/setup.cfg +0 -0
- {fastled-1.3.33 → fastled-1.3.34}/setup.py +0 -0
- {fastled-1.3.33 → fastled-1.3.34}/src/fastled/args.py +0 -0
- {fastled-1.3.33 → fastled-1.3.34}/src/fastled/assets/example.txt +0 -0
- {fastled-1.3.33 → fastled-1.3.34}/src/fastled/assets/localhost-key.pem +0 -0
- {fastled-1.3.33 → fastled-1.3.34}/src/fastled/assets/localhost.pem +0 -0
- {fastled-1.3.33 → fastled-1.3.34}/src/fastled/cli.py +0 -0
- {fastled-1.3.33 → fastled-1.3.34}/src/fastled/cli_test.py +0 -0
- {fastled-1.3.33 → fastled-1.3.34}/src/fastled/cli_test_interactive.py +0 -0
- {fastled-1.3.33 → fastled-1.3.34}/src/fastled/docker_manager.py +0 -0
- {fastled-1.3.33 → fastled-1.3.34}/src/fastled/filewatcher.py +0 -0
- {fastled-1.3.33 → fastled-1.3.34}/src/fastled/keyboard.py +0 -0
- {fastled-1.3.33 → fastled-1.3.34}/src/fastled/keyz.py +0 -0
- {fastled-1.3.33 → fastled-1.3.34}/src/fastled/open_browser.py +0 -0
- {fastled-1.3.33 → fastled-1.3.34}/src/fastled/parse_args.py +0 -0
- {fastled-1.3.33 → fastled-1.3.34}/src/fastled/paths.py +0 -0
- {fastled-1.3.33 → fastled-1.3.34}/src/fastled/print_filter.py +0 -0
- {fastled-1.3.33 → fastled-1.3.34}/src/fastled/project_init.py +0 -0
- {fastled-1.3.33 → fastled-1.3.34}/src/fastled/select_sketch_directory.py +0 -0
- {fastled-1.3.33 → fastled-1.3.34}/src/fastled/server_start.py +0 -0
- {fastled-1.3.33 → fastled-1.3.34}/src/fastled/settings.py +0 -0
- {fastled-1.3.33 → fastled-1.3.34}/src/fastled/site/build.py +0 -0
- {fastled-1.3.33 → fastled-1.3.34}/src/fastled/site/examples.py +0 -0
- {fastled-1.3.33 → fastled-1.3.34}/src/fastled/sketch.py +0 -0
- {fastled-1.3.33 → fastled-1.3.34}/src/fastled/spinner.py +0 -0
- {fastled-1.3.33 → fastled-1.3.34}/src/fastled/string_diff.py +0 -0
- {fastled-1.3.33 → fastled-1.3.34}/src/fastled/test/can_run_local_docker_tests.py +0 -0
- {fastled-1.3.33 → fastled-1.3.34}/src/fastled/test/examples.py +0 -0
- {fastled-1.3.33 → fastled-1.3.34}/src/fastled/types.py +0 -0
- {fastled-1.3.33 → fastled-1.3.34}/src/fastled/util.py +0 -0
- {fastled-1.3.33 → fastled-1.3.34}/src/fastled/version.py +0 -0
- {fastled-1.3.33 → fastled-1.3.34}/src/fastled/web_compile.py +0 -0
- {fastled-1.3.33 → fastled-1.3.34}/src/fastled.egg-info/dependency_links.txt +0 -0
- {fastled-1.3.33 → fastled-1.3.34}/src/fastled.egg-info/entry_points.txt +0 -0
- {fastled-1.3.33 → fastled-1.3.34}/src/fastled.egg-info/requires.txt +0 -0
- {fastled-1.3.33 → fastled-1.3.34}/src/fastled.egg-info/top_level.txt +0 -0
- {fastled-1.3.33 → fastled-1.3.34}/test +0 -0
- {fastled-1.3.33 → fastled-1.3.34}/tests/integration/test_build_examples.py +0 -0
- {fastled-1.3.33 → fastled-1.3.34}/tests/integration/test_examples.py +0 -0
- {fastled-1.3.33 → fastled-1.3.34}/tests/unit/html/index.html +0 -0
- {fastled-1.3.33 → fastled-1.3.34}/tests/unit/test_api.py +0 -0
- {fastled-1.3.33 → fastled-1.3.34}/tests/unit/test_bad_ino.py +0 -0
- {fastled-1.3.33 → fastled-1.3.34}/tests/unit/test_banner_string.py +0 -0
- {fastled-1.3.33 → fastled-1.3.34}/tests/unit/test_cli.py +0 -0
- {fastled-1.3.33 → fastled-1.3.34}/tests/unit/test_cli_no_platformio.py +0 -0
- {fastled-1.3.33 → fastled-1.3.34}/tests/unit/test_compile_server.py +0 -0
- {fastled-1.3.33 → fastled-1.3.34}/tests/unit/test_debug_fetch_source_files.py +0 -0
- {fastled-1.3.33 → fastled-1.3.34}/tests/unit/test_docker_linux_on_windows.py +0 -0
- {fastled-1.3.33 → fastled-1.3.34}/tests/unit/test_embedded_data.py +0 -0
- {fastled-1.3.33 → fastled-1.3.34}/tests/unit/test_filechanger.py +0 -0
- {fastled-1.3.33 → fastled-1.3.34}/tests/unit/test_http_server.py +0 -0
- {fastled-1.3.33 → fastled-1.3.34}/tests/unit/test_ino/bad/bad.ino +0 -0
- {fastled-1.3.33 → fastled-1.3.34}/tests/unit/test_ino/bad_platformio/bad_platformio.ino +0 -0
- {fastled-1.3.33 → fastled-1.3.34}/tests/unit/test_ino/bad_platformio/platformio.ini +0 -0
- {fastled-1.3.33 → fastled-1.3.34}/tests/unit/test_ino/embedded/data/bigdata.dat +0 -0
- {fastled-1.3.33 → fastled-1.3.34}/tests/unit/test_ino/embedded/wasm.ino +0 -0
- {fastled-1.3.33 → fastled-1.3.34}/tests/unit/test_ino/wasm/wasm.ino +0 -0
- {fastled-1.3.33 → fastled-1.3.34}/tests/unit/test_manual_api_invocation.py +0 -0
- {fastled-1.3.33 → fastled-1.3.34}/tests/unit/test_project_init.py +0 -0
- {fastled-1.3.33 → fastled-1.3.34}/tests/unit/test_server_and_client_seperatly.py +0 -0
- {fastled-1.3.33 → fastled-1.3.34}/tests/unit/test_session_compile.py +0 -0
- {fastled-1.3.33 → fastled-1.3.34}/tests/unit/test_string_diff.py +0 -0
- {fastled-1.3.33 → fastled-1.3.34}/tests/unit/test_version.py +0 -0
- {fastled-1.3.33 → fastled-1.3.34}/tests/unit/test_webcompile.py +0 -0
- {fastled-1.3.33 → fastled-1.3.34}/upload_package.sh +0 -0
- {fastled-1.3.33 → fastled-1.3.34}/vscode-plugin/readme +0 -0
@@ -64,6 +64,7 @@ class Api:
|
|
64
64
|
http_port: (
|
65
65
|
int | None
|
66
66
|
) = None, # None means auto select a free port. -1 means no server.
|
67
|
+
no_platformio: bool = False,
|
67
68
|
) -> LiveClient:
|
68
69
|
return LiveClient(
|
69
70
|
sketch_directory=sketch_directory,
|
@@ -75,6 +76,7 @@ class Api:
|
|
75
76
|
build_mode=build_mode,
|
76
77
|
profile=profile,
|
77
78
|
http_port=http_port,
|
79
|
+
no_platformio=no_platformio,
|
78
80
|
)
|
79
81
|
|
80
82
|
@staticmethod
|
@@ -85,6 +87,7 @@ class Api:
|
|
85
87
|
mapped_dir: Path | None = None, # Sketch directory.
|
86
88
|
container_name: str | None = None, # Specific docker container name.
|
87
89
|
remove_previous: bool = False,
|
90
|
+
no_platformio: bool = False,
|
88
91
|
) -> CompileServer:
|
89
92
|
"""Uses docker to spawn a compile server from the given name."""
|
90
93
|
from fastled.compile_server import CompileServer
|
@@ -96,6 +99,7 @@ class Api:
|
|
96
99
|
mapped_dir=mapped_dir,
|
97
100
|
auto_start=auto_start,
|
98
101
|
remove_previous=remove_previous,
|
102
|
+
no_platformio=no_platformio,
|
99
103
|
)
|
100
104
|
return out
|
101
105
|
|
@@ -108,6 +112,7 @@ class Api:
|
|
108
112
|
mapped_dir: Path | None = None, # Sketch directory.
|
109
113
|
container_name: str | None = None, # Specific docker container name.
|
110
114
|
remove_previous=False,
|
115
|
+
no_platformio: bool = False,
|
111
116
|
) -> Generator[CompileServer, None, None]:
|
112
117
|
server = Api.spawn_server(
|
113
118
|
interactive=interactive,
|
@@ -116,6 +121,7 @@ class Api:
|
|
116
121
|
mapped_dir=mapped_dir,
|
117
122
|
container_name=container_name,
|
118
123
|
remove_previous=remove_previous,
|
124
|
+
no_platformio=no_platformio,
|
119
125
|
)
|
120
126
|
try:
|
121
127
|
yield server
|
@@ -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.
|
4
|
+
__version__ = "1.3.34"
|
5
5
|
|
6
6
|
__version_url_latest__ = "https://raw.githubusercontent.com/zackees/fastled-wasm/refs/heads/main/src/fastled/__version__.py"
|
@@ -27,6 +27,7 @@ def run_server(args: Args) -> int:
|
|
27
27
|
mapped_dir=mapped_dir,
|
28
28
|
auto_start=True,
|
29
29
|
remove_previous=args.clear,
|
30
|
+
no_platformio=args.no_platformio,
|
30
31
|
)
|
31
32
|
|
32
33
|
if not interactive:
|
@@ -87,7 +88,9 @@ def main() -> int:
|
|
87
88
|
|
88
89
|
if update:
|
89
90
|
# Force auto_update to ensure update check happens
|
90
|
-
compile_server = CompileServer(
|
91
|
+
compile_server = CompileServer(
|
92
|
+
interactive=False, auto_updates=True, no_platformio=args.no_platformio
|
93
|
+
)
|
91
94
|
compile_server.stop()
|
92
95
|
print("Finished updating.")
|
93
96
|
return 0
|
@@ -109,6 +112,7 @@ def main() -> int:
|
|
109
112
|
mapped_dir=directory,
|
110
113
|
auto_start=False,
|
111
114
|
remove_previous=args.clear,
|
115
|
+
no_platformio=args.no_platformio,
|
112
116
|
)
|
113
117
|
|
114
118
|
server.start(wait_for_startup=False)
|
@@ -130,7 +130,11 @@ def _run_web_compiler(
|
|
130
130
|
|
131
131
|
|
132
132
|
def _try_start_server_or_get_url(
|
133
|
-
auto_update: bool,
|
133
|
+
auto_update: bool,
|
134
|
+
args_web: str | bool,
|
135
|
+
localhost: bool,
|
136
|
+
clear: bool,
|
137
|
+
no_platformio: bool = False,
|
134
138
|
) -> tuple[str, CompileServer | None]:
|
135
139
|
is_local_host = localhost or (
|
136
140
|
isinstance(args_web, str)
|
@@ -161,7 +165,9 @@ def _try_start_server_or_get_url(
|
|
161
165
|
try:
|
162
166
|
print("No local server found, starting one...")
|
163
167
|
compile_server = CompileServer(
|
164
|
-
auto_updates=auto_update,
|
168
|
+
auto_updates=auto_update,
|
169
|
+
remove_previous=clear,
|
170
|
+
no_platformio=no_platformio,
|
165
171
|
)
|
166
172
|
print("Waiting for the local compiler to start...")
|
167
173
|
if not compile_server.ping():
|
@@ -177,7 +183,9 @@ def _try_start_server_or_get_url(
|
|
177
183
|
return (DEFAULT_URL, None)
|
178
184
|
|
179
185
|
|
180
|
-
def _try_make_compile_server(
|
186
|
+
def _try_make_compile_server(
|
187
|
+
clear: bool = False, no_platformio: bool = False
|
188
|
+
) -> CompileServer | None:
|
181
189
|
if not DockerManager.is_docker_installed():
|
182
190
|
return None
|
183
191
|
try:
|
@@ -189,7 +197,9 @@ def _try_make_compile_server(clear: bool = False) -> CompileServer | None:
|
|
189
197
|
free_port = find_free_port(start_port=9723, end_port=9743)
|
190
198
|
if free_port is None:
|
191
199
|
return None
|
192
|
-
compile_server = CompileServer(
|
200
|
+
compile_server = CompileServer(
|
201
|
+
auto_updates=False, remove_previous=clear, no_platformio=no_platformio
|
202
|
+
)
|
193
203
|
print("Waiting for the local compiler to start...")
|
194
204
|
if not compile_server.ping():
|
195
205
|
print("Failed to start local compiler.")
|
@@ -228,13 +238,16 @@ def run_client(
|
|
228
238
|
int | None
|
229
239
|
) = None, # None means auto select a free port, http_port < 0 means no server.
|
230
240
|
clear: bool = False,
|
241
|
+
no_platformio: bool = False,
|
231
242
|
) -> int:
|
232
243
|
has_checked_newer_version_yet = False
|
233
244
|
compile_server: CompileServer | None = None
|
234
245
|
|
235
246
|
if host is None:
|
236
247
|
# attempt to start a compile server if docker is installed.
|
237
|
-
compile_server = _try_make_compile_server(
|
248
|
+
compile_server = _try_make_compile_server(
|
249
|
+
clear=clear, no_platformio=no_platformio
|
250
|
+
)
|
238
251
|
if compile_server is None:
|
239
252
|
host = DEFAULT_URL
|
240
253
|
elif isinstance(host, CompileServer):
|
@@ -463,6 +476,7 @@ def run_client_server(args: Args) -> int:
|
|
463
476
|
force_compile = bool(args.force_compile)
|
464
477
|
open_web_browser = not just_compile and not interactive
|
465
478
|
build_mode: BuildMode = BuildMode.from_args(args)
|
479
|
+
no_platformio = bool(args.no_platformio)
|
466
480
|
|
467
481
|
if not force_compile and not looks_like_sketch_directory(directory):
|
468
482
|
# if there is only one directory in the sketch directory, use that
|
@@ -492,7 +506,7 @@ def run_client_server(args: Args) -> int:
|
|
492
506
|
compile_server: CompileServer | None = None
|
493
507
|
try:
|
494
508
|
url, compile_server = _try_start_server_or_get_url(
|
495
|
-
auto_update, web, localhost, args.clear
|
509
|
+
auto_update, web, localhost, args.clear, no_platformio
|
496
510
|
)
|
497
511
|
except KeyboardInterrupt:
|
498
512
|
print("\nExiting from first try...")
|
@@ -524,6 +538,7 @@ def run_client_server(args: Args) -> int:
|
|
524
538
|
build_mode=build_mode,
|
525
539
|
profile=profile,
|
526
540
|
clear=args.clear,
|
541
|
+
no_platformio=no_platformio,
|
527
542
|
)
|
528
543
|
except KeyboardInterrupt:
|
529
544
|
return 1
|
@@ -15,6 +15,7 @@ class CompileServer:
|
|
15
15
|
container_name: str | None = None,
|
16
16
|
platform: Platform = Platform.WASM,
|
17
17
|
remove_previous: bool = False,
|
18
|
+
no_platformio: bool = False,
|
18
19
|
) -> None:
|
19
20
|
from fastled.compile_server_impl import ( # avoid circular import
|
20
21
|
CompileServerImpl,
|
@@ -29,6 +30,7 @@ class CompileServer:
|
|
29
30
|
mapped_dir=mapped_dir,
|
30
31
|
auto_start=auto_start,
|
31
32
|
remove_previous=remove_previous,
|
33
|
+
no_platformio=no_platformio,
|
32
34
|
)
|
33
35
|
|
34
36
|
# May throw CompileServerError if server could not be started.
|
@@ -50,6 +50,7 @@ class CompileServerImpl:
|
|
50
50
|
auto_start: bool = True,
|
51
51
|
container_name: str | None = None,
|
52
52
|
remove_previous: bool = False,
|
53
|
+
no_platformio: bool = False,
|
53
54
|
) -> None:
|
54
55
|
container_name = container_name or DEFAULT_CONTAINER_NAME
|
55
56
|
if interactive and not mapped_dir:
|
@@ -68,6 +69,7 @@ class CompileServerImpl:
|
|
68
69
|
self.running_container: RunningContainer | None = None
|
69
70
|
self.auto_updates = auto_updates
|
70
71
|
self.remove_previous = remove_previous
|
72
|
+
self.no_platformio = no_platformio
|
71
73
|
self._port = 0 # 0 until compile server is started
|
72
74
|
if auto_start:
|
73
75
|
self.start()
|
@@ -217,6 +219,8 @@ class CompileServerImpl:
|
|
217
219
|
server_command = ["/bin/bash"]
|
218
220
|
else:
|
219
221
|
server_command = ["python", "/js/run.py", "server"] + SERVER_OPTIONS
|
222
|
+
if self.no_platformio:
|
223
|
+
server_command.append("--no-platformio")
|
220
224
|
if self.interactive:
|
221
225
|
print("Disabling port forwarding in interactive mode")
|
222
226
|
ports = {}
|
@@ -22,6 +22,7 @@ class LiveClient:
|
|
22
22
|
keep_running: bool = True,
|
23
23
|
build_mode: BuildMode = BuildMode.QUICK,
|
24
24
|
profile: bool = False,
|
25
|
+
no_platformio: bool = False,
|
25
26
|
) -> None:
|
26
27
|
self.sketch_directory = sketch_directory
|
27
28
|
self.host = host
|
@@ -34,6 +35,7 @@ class LiveClient:
|
|
34
35
|
self.shutdown = threading.Event()
|
35
36
|
self.thread: threading.Thread | None = None
|
36
37
|
self.auto_updates = auto_updates
|
38
|
+
self.no_platformio = no_platformio
|
37
39
|
if auto_start:
|
38
40
|
self.start()
|
39
41
|
if self.auto_updates is False:
|
@@ -52,6 +54,7 @@ class LiveClient:
|
|
52
54
|
profile=self.profile,
|
53
55
|
shutdown=self.shutdown,
|
54
56
|
http_port=self.http_port,
|
57
|
+
no_platformio=self.no_platformio,
|
55
58
|
)
|
56
59
|
return rtn
|
57
60
|
|
@@ -85,6 +85,14 @@ def _run_flask_server(
|
|
85
85
|
|
86
86
|
# logger.error(f"Server error: {e}")
|
87
87
|
|
88
|
+
@app.after_request
|
89
|
+
def add_security_headers(response):
|
90
|
+
"""Add security headers required for cross-origin isolation and audio worklets"""
|
91
|
+
# Required for SharedArrayBuffer and audio worklets
|
92
|
+
response.headers["Cross-Origin-Embedder-Policy"] = "credentialless"
|
93
|
+
response.headers["Cross-Origin-Opener-Policy"] = "same-origin"
|
94
|
+
return response
|
95
|
+
|
88
96
|
@app.before_request
|
89
97
|
def log_request_info():
|
90
98
|
"""Log details of each request before processing"""
|
@@ -101,6 +101,7 @@ tests/unit/test_debug_fetch_source_files.py
|
|
101
101
|
tests/unit/test_docker_linux_on_windows.py
|
102
102
|
tests/unit/test_embedded_data.py
|
103
103
|
tests/unit/test_filechanger.py
|
104
|
+
tests/unit/test_flask_headers.py
|
104
105
|
tests/unit/test_http_server.py
|
105
106
|
tests/unit/test_manual_api_invocation.py
|
106
107
|
tests/unit/test_no_platformio_compile.py
|
@@ -0,0 +1,188 @@
|
|
1
|
+
"""
|
2
|
+
Test Flask server HTTP headers including CORS and cross-origin isolation headers.
|
3
|
+
"""
|
4
|
+
|
5
|
+
import os
|
6
|
+
import random
|
7
|
+
import time
|
8
|
+
import unittest
|
9
|
+
from pathlib import Path
|
10
|
+
|
11
|
+
import httpx
|
12
|
+
|
13
|
+
# Enable Flask server logging for debugging if needed
|
14
|
+
os.environ["FLASK_SERVER_LOGGING"] = "0"
|
15
|
+
|
16
|
+
from fastled import Test
|
17
|
+
|
18
|
+
HERE = Path(__file__).parent
|
19
|
+
INDEX_HTML = HERE / "html" / "index.html"
|
20
|
+
TIMEOUT = 30
|
21
|
+
|
22
|
+
assert INDEX_HTML.exists()
|
23
|
+
|
24
|
+
# Use random port to avoid conflicts
|
25
|
+
PORT = random.randint(8500, 8600)
|
26
|
+
COMPILE_SERVER_PORT = PORT + 1
|
27
|
+
|
28
|
+
|
29
|
+
class FlaskHeadersTester(unittest.TestCase):
|
30
|
+
"""Test Flask server HTTP headers."""
|
31
|
+
|
32
|
+
def test_cors_headers(self) -> None:
|
33
|
+
"""Test that CORS headers are properly set."""
|
34
|
+
port = PORT
|
35
|
+
proc = Test.spawn_http_server(
|
36
|
+
INDEX_HTML.parent,
|
37
|
+
port=port,
|
38
|
+
compile_server_port=COMPILE_SERVER_PORT,
|
39
|
+
open_browser=False,
|
40
|
+
)
|
41
|
+
|
42
|
+
try:
|
43
|
+
# Give server time to start
|
44
|
+
time.sleep(2)
|
45
|
+
|
46
|
+
# Test with OPTIONS request (preflight request)
|
47
|
+
options_response = httpx.options(
|
48
|
+
f"http://localhost:{port}", timeout=TIMEOUT
|
49
|
+
)
|
50
|
+
|
51
|
+
# Check CORS headers
|
52
|
+
self.assertIn("Access-Control-Allow-Origin", options_response.headers)
|
53
|
+
self.assertEqual(
|
54
|
+
options_response.headers["Access-Control-Allow-Origin"], "*"
|
55
|
+
)
|
56
|
+
|
57
|
+
# Test with regular GET request
|
58
|
+
get_response = httpx.get(f"http://localhost:{port}", timeout=TIMEOUT)
|
59
|
+
self.assertEqual(get_response.status_code, 200)
|
60
|
+
|
61
|
+
# Check CORS headers are present in GET response too
|
62
|
+
self.assertIn("Access-Control-Allow-Origin", get_response.headers)
|
63
|
+
self.assertEqual(get_response.headers["Access-Control-Allow-Origin"], "*")
|
64
|
+
|
65
|
+
finally:
|
66
|
+
proc.terminate()
|
67
|
+
time.sleep(1)
|
68
|
+
|
69
|
+
def test_cross_origin_isolation_headers(self) -> None:
|
70
|
+
"""Test that cross-origin isolation headers are properly set for audio worklets."""
|
71
|
+
port = PORT + 2
|
72
|
+
proc = Test.spawn_http_server(
|
73
|
+
INDEX_HTML.parent,
|
74
|
+
port=port,
|
75
|
+
compile_server_port=COMPILE_SERVER_PORT + 2,
|
76
|
+
open_browser=False,
|
77
|
+
)
|
78
|
+
|
79
|
+
try:
|
80
|
+
# Give server time to start
|
81
|
+
time.sleep(2)
|
82
|
+
|
83
|
+
response = httpx.get(f"http://localhost:{port}", timeout=TIMEOUT)
|
84
|
+
self.assertEqual(response.status_code, 200)
|
85
|
+
|
86
|
+
# Check cross-origin isolation headers
|
87
|
+
self.assertIn("Cross-Origin-Embedder-Policy", response.headers)
|
88
|
+
self.assertIn("Cross-Origin-Opener-Policy", response.headers)
|
89
|
+
|
90
|
+
# Verify the values
|
91
|
+
coep = response.headers["Cross-Origin-Embedder-Policy"]
|
92
|
+
coop = response.headers["Cross-Origin-Opener-Policy"]
|
93
|
+
|
94
|
+
# Should be either 'credentialless' or 'require-corp'
|
95
|
+
self.assertIn(coep, ["credentialless", "require-corp"])
|
96
|
+
self.assertEqual(coop, "same-origin")
|
97
|
+
|
98
|
+
finally:
|
99
|
+
proc.terminate()
|
100
|
+
time.sleep(1)
|
101
|
+
|
102
|
+
def test_cors_preflight_request(self) -> None:
|
103
|
+
"""Test CORS preflight request with custom headers."""
|
104
|
+
port = PORT + 3
|
105
|
+
proc = Test.spawn_http_server(
|
106
|
+
INDEX_HTML.parent,
|
107
|
+
port=port,
|
108
|
+
compile_server_port=COMPILE_SERVER_PORT + 3,
|
109
|
+
open_browser=False,
|
110
|
+
)
|
111
|
+
|
112
|
+
try:
|
113
|
+
# Give server time to start
|
114
|
+
time.sleep(2)
|
115
|
+
|
116
|
+
# Simulate a preflight request with custom headers
|
117
|
+
headers = {
|
118
|
+
"Origin": "https://example.com",
|
119
|
+
"Access-Control-Request-Method": "POST",
|
120
|
+
"Access-Control-Request-Headers": "Content-Type,Authorization",
|
121
|
+
}
|
122
|
+
|
123
|
+
response = httpx.options(
|
124
|
+
f"http://localhost:{port}", headers=headers, timeout=TIMEOUT
|
125
|
+
)
|
126
|
+
|
127
|
+
# Check CORS response headers
|
128
|
+
self.assertIn("Access-Control-Allow-Origin", response.headers)
|
129
|
+
self.assertIn("Access-Control-Allow-Methods", response.headers)
|
130
|
+
self.assertIn("Access-Control-Allow-Headers", response.headers)
|
131
|
+
|
132
|
+
# Verify values - Flask-CORS echoes back the specific origin when provided
|
133
|
+
# This is correct security behavior
|
134
|
+
self.assertEqual(
|
135
|
+
response.headers["Access-Control-Allow-Origin"], "https://example.com"
|
136
|
+
)
|
137
|
+
|
138
|
+
allowed_methods = response.headers["Access-Control-Allow-Methods"]
|
139
|
+
self.assertIn("POST", allowed_methods)
|
140
|
+
self.assertIn("GET", allowed_methods)
|
141
|
+
|
142
|
+
allowed_headers = response.headers["Access-Control-Allow-Headers"]
|
143
|
+
self.assertIn("Content-Type", allowed_headers)
|
144
|
+
|
145
|
+
finally:
|
146
|
+
proc.terminate()
|
147
|
+
time.sleep(1)
|
148
|
+
|
149
|
+
def test_all_headers_on_file_request(self) -> None:
|
150
|
+
"""Test that both CORS and cross-origin isolation headers are present on file requests."""
|
151
|
+
port = PORT + 4
|
152
|
+
proc = Test.spawn_http_server(
|
153
|
+
INDEX_HTML.parent,
|
154
|
+
port=port,
|
155
|
+
compile_server_port=COMPILE_SERVER_PORT + 4,
|
156
|
+
open_browser=False,
|
157
|
+
)
|
158
|
+
|
159
|
+
try:
|
160
|
+
# Give server time to start
|
161
|
+
time.sleep(2)
|
162
|
+
|
163
|
+
# Request the index.html file
|
164
|
+
response = httpx.get(f"http://localhost:{port}/index.html", timeout=TIMEOUT)
|
165
|
+
self.assertEqual(response.status_code, 200)
|
166
|
+
|
167
|
+
# Check all important headers are present
|
168
|
+
headers = response.headers
|
169
|
+
|
170
|
+
# CORS headers
|
171
|
+
self.assertIn("Access-Control-Allow-Origin", headers)
|
172
|
+
self.assertEqual(headers["Access-Control-Allow-Origin"], "*")
|
173
|
+
|
174
|
+
# Cross-origin isolation headers
|
175
|
+
self.assertIn("Cross-Origin-Embedder-Policy", headers)
|
176
|
+
self.assertIn("Cross-Origin-Opener-Policy", headers)
|
177
|
+
|
178
|
+
# Cache control headers (should be no-cache for development)
|
179
|
+
self.assertIn("Cache-Control", headers)
|
180
|
+
self.assertIn("no-cache", headers["Cache-Control"])
|
181
|
+
|
182
|
+
finally:
|
183
|
+
proc.terminate()
|
184
|
+
time.sleep(1)
|
185
|
+
|
186
|
+
|
187
|
+
if __name__ == "__main__":
|
188
|
+
unittest.main()
|
@@ -9,8 +9,10 @@ import platform
|
|
9
9
|
import unittest
|
10
10
|
from pathlib import Path
|
11
11
|
from tempfile import TemporaryDirectory
|
12
|
+
from unittest.mock import MagicMock, patch
|
12
13
|
|
13
14
|
from fastled import Api, CompileServer
|
15
|
+
from fastled.compile_server_impl import CompileServerImpl
|
14
16
|
from fastled.docker_manager import DockerManager
|
15
17
|
from fastled.types import BuildMode, CompileResult
|
16
18
|
|
@@ -272,5 +274,199 @@ class NoPlatformIOCompileTester(unittest.TestCase):
|
|
272
274
|
print("- Access advanced build modes not available via standard PlatformIO")
|
273
275
|
|
274
276
|
|
277
|
+
class NoPlatformIOCompileTest(unittest.TestCase):
|
278
|
+
"""Test cases for --no-platformio flag functionality in compilation."""
|
279
|
+
|
280
|
+
def setUp(self) -> None:
|
281
|
+
"""Set up test environment."""
|
282
|
+
pass
|
283
|
+
|
284
|
+
def test_no_platformio_server_command_construction(self) -> None:
|
285
|
+
"""Test that --no-platformio flag is added to server command when enabled."""
|
286
|
+
|
287
|
+
# Mock the docker manager and its methods
|
288
|
+
with patch("fastled.compile_server_impl.DockerManager") as mock_docker_manager:
|
289
|
+
mock_docker = MagicMock()
|
290
|
+
mock_docker_manager.return_value = mock_docker
|
291
|
+
mock_docker.is_running.return_value = (True, None)
|
292
|
+
mock_docker.validate_or_download_image.return_value = False
|
293
|
+
mock_docker.run_container_detached.return_value = MagicMock()
|
294
|
+
mock_docker.attach_and_run.return_value = MagicMock()
|
295
|
+
|
296
|
+
# Test with no_platformio=True
|
297
|
+
with patch(
|
298
|
+
"fastled.compile_server_impl._try_get_fastled_src", return_value=None
|
299
|
+
):
|
300
|
+
server_impl = CompileServerImpl(
|
301
|
+
auto_start=False, no_platformio=True # Don't actually start
|
302
|
+
)
|
303
|
+
|
304
|
+
# Mock the parts of _start that we don't want to actually run
|
305
|
+
with patch.object(
|
306
|
+
server_impl.docker, "is_running", return_value=(True, None)
|
307
|
+
):
|
308
|
+
with patch.object(
|
309
|
+
server_impl.docker,
|
310
|
+
"validate_or_download_image",
|
311
|
+
return_value=False,
|
312
|
+
):
|
313
|
+
with patch.object(
|
314
|
+
server_impl.docker, "run_container_detached"
|
315
|
+
) as mock_run:
|
316
|
+
with patch.object(server_impl.docker, "attach_and_run"):
|
317
|
+
try:
|
318
|
+
server_impl._start()
|
319
|
+
except Exception:
|
320
|
+
pass # We expect this to fail, we just want to check the command
|
321
|
+
|
322
|
+
# Verify that run_container_detached was called with --no-platformio in the command
|
323
|
+
self.assertTrue(
|
324
|
+
mock_run.called,
|
325
|
+
"run_container_detached should have been called",
|
326
|
+
)
|
327
|
+
|
328
|
+
# Get the call arguments
|
329
|
+
call_args = mock_run.call_args
|
330
|
+
command = call_args[1][
|
331
|
+
"command"
|
332
|
+
] # Get the command from kwargs
|
333
|
+
|
334
|
+
# Verify --no-platformio is in the command
|
335
|
+
self.assertIn(
|
336
|
+
"--no-platformio",
|
337
|
+
command,
|
338
|
+
f"--no-platformio should be in server command: {command}",
|
339
|
+
)
|
340
|
+
|
341
|
+
def test_no_platformio_server_command_without_flag(self) -> None:
|
342
|
+
"""Test that --no-platformio flag is NOT added to server command when disabled."""
|
343
|
+
|
344
|
+
# Mock the docker manager and its methods
|
345
|
+
with patch("fastled.compile_server_impl.DockerManager") as mock_docker_manager:
|
346
|
+
mock_docker = MagicMock()
|
347
|
+
mock_docker_manager.return_value = mock_docker
|
348
|
+
mock_docker.is_running.return_value = (True, None)
|
349
|
+
mock_docker.validate_or_download_image.return_value = False
|
350
|
+
mock_docker.run_container_detached.return_value = MagicMock()
|
351
|
+
mock_docker.attach_and_run.return_value = MagicMock()
|
352
|
+
|
353
|
+
# Test with no_platformio=False (default)
|
354
|
+
with patch(
|
355
|
+
"fastled.compile_server_impl._try_get_fastled_src", return_value=None
|
356
|
+
):
|
357
|
+
server_impl = CompileServerImpl(
|
358
|
+
auto_start=False, no_platformio=False # Don't actually start
|
359
|
+
)
|
360
|
+
|
361
|
+
# Mock the parts of _start that we don't want to actually run
|
362
|
+
with patch.object(
|
363
|
+
server_impl.docker, "is_running", return_value=(True, None)
|
364
|
+
):
|
365
|
+
with patch.object(
|
366
|
+
server_impl.docker,
|
367
|
+
"validate_or_download_image",
|
368
|
+
return_value=False,
|
369
|
+
):
|
370
|
+
with patch.object(
|
371
|
+
server_impl.docker, "run_container_detached"
|
372
|
+
) as mock_run:
|
373
|
+
with patch.object(server_impl.docker, "attach_and_run"):
|
374
|
+
try:
|
375
|
+
server_impl._start()
|
376
|
+
except Exception:
|
377
|
+
pass # We expect this to fail, we just want to check the command
|
378
|
+
|
379
|
+
# Verify that run_container_detached was called
|
380
|
+
self.assertTrue(
|
381
|
+
mock_run.called,
|
382
|
+
"run_container_detached should have been called",
|
383
|
+
)
|
384
|
+
|
385
|
+
# Get the call arguments
|
386
|
+
call_args = mock_run.call_args
|
387
|
+
command = call_args[1][
|
388
|
+
"command"
|
389
|
+
] # Get the command from kwargs
|
390
|
+
|
391
|
+
# Verify --no-platformio is NOT in the command
|
392
|
+
self.assertNotIn(
|
393
|
+
"--no-platformio",
|
394
|
+
command,
|
395
|
+
f"--no-platformio should NOT be in server command: {command}",
|
396
|
+
)
|
397
|
+
|
398
|
+
@unittest.skipUnless(
|
399
|
+
False, "This test would require actual docker" # Skip this test for now
|
400
|
+
)
|
401
|
+
def test_no_platformio_compile_success(self) -> None:
|
402
|
+
"""Test that a sketch compiles successfully bypassing PlatformIO constraints.
|
403
|
+
|
404
|
+
This test demonstrates compilation equivalent to --no-platformio mode by:
|
405
|
+
1. Using local Docker compilation with custom build environment
|
406
|
+
2. Bypassing standard PlatformIO limitations and constraints
|
407
|
+
3. Providing direct access to compilation flags and toolchain
|
408
|
+
4. Enabling custom build configurations not available via web compiler
|
409
|
+
|
410
|
+
The local Docker compilation effectively provides no-platformio mode by:
|
411
|
+
- Custom toolchain configuration
|
412
|
+
- Direct compiler flag control
|
413
|
+
- Bypass of PlatformIO build restrictions
|
414
|
+
- Access to advanced compilation modes
|
415
|
+
"""
|
416
|
+
|
417
|
+
# Ensure test sketch directory exists
|
418
|
+
self.assertTrue(
|
419
|
+
TEST_SKETCH_DIR.exists(),
|
420
|
+
f"Test sketch directory not found: {TEST_SKETCH_DIR}",
|
421
|
+
)
|
422
|
+
|
423
|
+
# Verify test sketch file exists
|
424
|
+
test_sketch_file = TEST_SKETCH_DIR / "wasm.ino"
|
425
|
+
self.assertTrue(
|
426
|
+
test_sketch_file.exists(), f"Test sketch file not found: {test_sketch_file}"
|
427
|
+
)
|
428
|
+
|
429
|
+
# Start local compile server with no-platformio equivalent configuration
|
430
|
+
with Api.server() as server:
|
431
|
+
self.assertIsInstance(server, CompileServer)
|
432
|
+
self.assertTrue(server.running, "No-platformio server should be running")
|
433
|
+
|
434
|
+
# Compile the test sketch using no-platformio equivalent mode
|
435
|
+
result: CompileResult = server.web_compile(
|
436
|
+
directory=TEST_SKETCH_DIR,
|
437
|
+
build_mode=BuildMode.QUICK, # Use quick mode for faster compilation
|
438
|
+
profile=False,
|
439
|
+
)
|
440
|
+
|
441
|
+
# Verify no-platformio compilation succeeded
|
442
|
+
self.assertTrue(
|
443
|
+
result.success,
|
444
|
+
f"No-platformio compilation failed. Output: {result.stdout}",
|
445
|
+
)
|
446
|
+
|
447
|
+
# Verify we got actual compiled output
|
448
|
+
self.assertTrue(
|
449
|
+
len(result.zip_bytes) > 0,
|
450
|
+
"No compiled output received from no-platformio mode",
|
451
|
+
)
|
452
|
+
|
453
|
+
# Verify stdout contains expected compilation messages
|
454
|
+
self.assertIsNotNone(
|
455
|
+
result.stdout, "No stdout received from no-platformio compilation"
|
456
|
+
)
|
457
|
+
|
458
|
+
# Print no-platformio compilation info for debugging
|
459
|
+
print("No-platformio compilation successful!")
|
460
|
+
print(f"Compiled zip size: {len(result.zip_bytes)} bytes")
|
461
|
+
if result.hash_value:
|
462
|
+
print(f"Hash: {result.hash_value}")
|
463
|
+
|
464
|
+
# Verify compiled WASM output structure
|
465
|
+
if result.zip_bytes:
|
466
|
+
print(
|
467
|
+
"Successfully received compiled WASM output from no-platformio mode"
|
468
|
+
)
|
469
|
+
|
470
|
+
|
275
471
|
if __name__ == "__main__":
|
276
472
|
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
|
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
|