fastled 1.4.9__py3-none-any.whl → 1.4.10__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 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.9"
4
+ __version__ = "1.4.10"
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
+ )
fastled/open_browser.py CHANGED
@@ -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 install_playwright_browsers
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
 
fastled/web_compile.py CHANGED
@@ -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 # 2 mins timeout
19
+ _TIMEOUT = 60 * 4 # 4 mins timeout
19
20
  _AUTH_TOKEN = "oBOT5jbsO4ztgrpNsQwlmFLIKB"
20
21
 
21
22
 
@@ -83,25 +84,26 @@ def _compile_libfastled(
83
84
  httpx.HTTPTransport(local_address="0.0.0.0") if connection_result.ipv4 else None
84
85
  )
85
86
 
86
- with httpx.Client(
87
+ headers = {
88
+ "accept": "application/json",
89
+ "authorization": auth_token,
90
+ "build": build_mode.value.lower(),
91
+ }
92
+
93
+ url = f"{connection_result.host}/compile/libfastled"
94
+ print(f"Compiling libfastled on {url} via {ipv4_stmt}")
95
+
96
+ # Use interruptible HTTP request
97
+ response = make_interruptible_post_request(
98
+ url=url,
99
+ files={}, # No files for libfastled compilation
100
+ headers=headers,
87
101
  transport=transport,
88
102
  timeout=_TIMEOUT * 2, # Give more time for library compilation
89
- ) as client:
90
- headers = {
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
- )
103
+ follow_redirects=False,
104
+ )
103
105
 
104
- return response
106
+ return response
105
107
 
106
108
 
107
109
  def _send_compile_request(
@@ -131,37 +133,32 @@ def _send_compile_request(
131
133
 
132
134
  archive_size = len(zip_bytes)
133
135
 
134
- with httpx.Client(
136
+ headers = {
137
+ "accept": "application/json",
138
+ "authorization": auth_token,
139
+ "build": (
140
+ build_mode.value.lower() if build_mode else BuildMode.QUICK.value.lower()
141
+ ),
142
+ "profile": "true" if profile else "false",
143
+ "no-platformio": "true" if no_platformio else "false",
144
+ "allow-libcompile": "false", # Always false since we handle it manually
145
+ }
146
+
147
+ url = f"{connection_result.host}/{ENDPOINT_COMPILED_WASM}"
148
+ print(f"Compiling sketch on {url} via {ipv4_stmt}. Zip size: {archive_size} bytes")
149
+ files = {"file": ("wasm.zip", zip_bytes, "application/x-zip-compressed")}
150
+
151
+ # Use interruptible HTTP request
152
+ response = make_interruptible_post_request(
153
+ url=url,
154
+ files=files,
155
+ headers=headers,
135
156
  transport=transport,
136
157
  timeout=_TIMEOUT,
137
- ) as client:
138
- headers = {
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
- )
158
+ follow_redirects=True,
159
+ )
163
160
 
164
- return response
161
+ return response
165
162
 
166
163
 
167
164
  def _process_compile_response(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fastled
3
- Version: 1.4.9
3
+ Version: 1.4.10
4
4
  Summary: FastLED Wasm Compiler
5
5
  Home-page: https://github.com/zackees/fastled-wasm
6
6
  Maintainer: Zachary Vorhies
@@ -1,9 +1,8 @@
1
1
  fastled/__init__.py,sha256=dahiY41HLLotTjqmpVJmXSwUEp8NKqoZ57jt55hBLa4,7667
2
2
  fastled/__main__.py,sha256=OcKv2ER1_iQAsZzLIUb3C8hRC9L2clNOhCrjpshrlf4,336
3
- fastled/__version__.py,sha256=ZiZk3FJ43XnaDFc89u1d82Mxs7giHFYqSujWneamokY,372
3
+ fastled/__version__.py,sha256=79y56vY5gQySq33JTd8MFfXtNmylXNSfvohTya9mP_M,373
4
4
  fastled/app.py,sha256=6XOuObi72AUnZXASDOVbcSflr4He0xnIDk5P8nVmVus,6131
5
5
  fastled/args.py,sha256=d8Afa7NMcNLMIQqIBX_ZOL5JOyeJ7XCch4LdkhFNChk,3671
6
- fastled/chrome_extension_downloader.py,sha256=48YyQrsuK1TVXPuAvRGzqkQJnx0991Ka6OVUo1A58zU,7079
7
6
  fastled/cli.py,sha256=drgR2AOxVrj3QEz58iiKscYAumbbin2vIV-k91VCOAA,561
8
7
  fastled/cli_test.py,sha256=W-1nODZrip_JU6BEbYhxOa4ckxduOsiX8zIoRkTyxv4,550
9
8
  fastled/cli_test_interactive.py,sha256=BjNhveZOk5aCffHbcrxPQQjWmAuj4ClVKKcKX5eY6yM,542
@@ -13,13 +12,13 @@ fastled/compile_server_impl.py,sha256=iCwNCs7YxypUuVPmY4979mOgoH9OiuAJa1a1bmpG1c
13
12
  fastled/docker_manager.py,sha256=rkq39ZKrU6NHIyDa3mzs0Unb6o9oMeAwxhqiuHJU_RY,40291
14
13
  fastled/filewatcher.py,sha256=gEcJJHTDJ1X3gKJzltmEBhixWGbZj2eJD7a4vwSvITQ,10036
15
14
  fastled/find_good_connection.py,sha256=xnrJjrbwNZUkvSQRn_ZTMoVh5GBWTbO-lEsr_L95xq8,3372
15
+ fastled/interruptible_http.py,sha256=2QwUsRNJ1qawf_-Lp1l0dBady3TK0SrBFhmnWgM7oqg,4888
16
16
  fastled/keyboard.py,sha256=UTAsqCn1UMYnB8YDzENiLTj4GeL45tYfEcO7_5fLFEg,3556
17
17
  fastled/keyz.py,sha256=LO-8m_7CpNDiZLM-FXhQ30f9gN1bUYz5lOsUPTIbI-c,4020
18
18
  fastled/live_client.py,sha256=yp_ujG92EHYpSedGOUteuG2nQvMKbp1GbUpgQ6nU4Dc,3083
19
- fastled/open_browser.py,sha256=gtBF7hoc0qcmWM_om0crcJ26TsmX5sRTCulaVRQLjP8,5023
19
+ fastled/open_browser.py,sha256=N0d_D87sSBOUbBUl7gktJufdyM1laEKIERCXnEYCyu4,5086
20
20
  fastled/parse_args.py,sha256=UiGgFoR_7ZCVC26rxE4FpxpmPu9xBhiiCD4pwmY_gLw,11520
21
21
  fastled/paths.py,sha256=VsPmgu0lNSCFOoEC0BsTYzDygXqy15AHUfN-tTuzDZA,99
22
- fastled/playwright_browser.py,sha256=qsPQiwamSOSBuXPoqgr1ybIRZbjJH5MeoMNLjpR4aTg,26776
23
22
  fastled/print_filter.py,sha256=nc_rqYYdCUPinFycaK7fiQF5PG1up51pmJptR__QyAs,1499
24
23
  fastled/project_init.py,sha256=bBt4DwmW5hZkm9ICt9Qk-0Nr_0JQM7icCgH5Iv-bCQs,3984
25
24
  fastled/select_sketch_directory.py,sha256=-eudwCns3AKj4HuHtSkZAFwbnf005SNL07pOzs9VxnE,1383
@@ -32,18 +31,20 @@ fastled/string_diff.py,sha256=oTncu0qYdLlLUtYLLDB4bzdQ2OfzegAR6XNAzwE9fIs,6002
32
31
  fastled/types.py,sha256=ZDf1TbTT4XgA_pKIwr4JbkDB38_29ogSdDORjoT-zuY,1803
33
32
  fastled/util.py,sha256=TjhXbUNh4p2BGhNAldSeL68B7BBOjsWAXji5gy-vDEQ,1440
34
33
  fastled/version.py,sha256=TpBMiEVdO3_sUZEu6wmwN8Q4AgX2BiCxStCsnPKh6E0,1209
35
- fastled/web_compile.py,sha256=Ql2DBRInZy7dOr1WZiUlhdg1ZVuU1nkbndRWiq7iENQ,10002
34
+ fastled/web_compile.py,sha256=8wJ6EK01hHsQLfeon4_TQaEDQZjsHHPidPPIzjMYUuM,9960
36
35
  fastled/zip_files.py,sha256=BgHFjaLJ7wF6mnzjqOgn76VcKDwhwc_-w_qyUG_-aNs,2815
37
36
  fastled/assets/example.txt,sha256=lTBovRjiz0_TgtAtbA1C5hNi2ffbqnNPqkKg6UiKCT8,54
38
37
  fastled/assets/localhost-key.pem,sha256=Q-CNO_UoOd8fFNN4ljcnqwUeCMhzTplRjLO2x0pYRlU,1704
39
38
  fastled/assets/localhost.pem,sha256=QTwUtTwjYWbm9m3pHW2IlK2nFZJ8b0pppxPjhgVZqQo,1619
39
+ fastled/playwright/chrome_extension_downloader.py,sha256=48YyQrsuK1TVXPuAvRGzqkQJnx0991Ka6OVUo1A58zU,7079
40
+ fastled/playwright/playwright_browser.py,sha256=5cx80kEV72CPlBT9fIcJNc9z40EFSOfXchBKBE9R4sE,26798
40
41
  fastled/site/build.py,sha256=2YKU_UWKlJdGnjdbAbaL0co6kceFMSTVYwH1KCmgPZA,13987
41
42
  fastled/site/examples.py,sha256=s6vj2zJc6BfKlnbwXr1QWY1mzuDBMt6j5MEBOWjO_U8,155
42
43
  fastled/test/can_run_local_docker_tests.py,sha256=LEuUbHctRhNNFWcvnz2kEGmjDJeXO4c3kNpizm3yVJs,400
43
44
  fastled/test/examples.py,sha256=GfaHeY1E8izBl6ZqDVjz--RHLyVR4NRnQ5pBesCFJFY,1673
44
- fastled-1.4.9.dist-info/licenses/LICENSE,sha256=b6pOoifSXiUaz_lDS84vWlG3fr4yUKwB8fzkrH9R8bQ,1064
45
- fastled-1.4.9.dist-info/METADATA,sha256=y9Q00P53Nh_2KnIKWDMuKBrcq5_JqAp8MlmYf0PvwEw,31909
46
- fastled-1.4.9.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
47
- fastled-1.4.9.dist-info/entry_points.txt,sha256=RCwmzCSOS4-C2i9EziANq7Z2Zb4KFnEMR1FQC0bBwAw,101
48
- fastled-1.4.9.dist-info/top_level.txt,sha256=Bbv5kpJpZhWNCvDF4K0VcvtBSDMa8B7PTOrZa9CezHY,8
49
- fastled-1.4.9.dist-info/RECORD,,
45
+ fastled-1.4.10.dist-info/licenses/LICENSE,sha256=b6pOoifSXiUaz_lDS84vWlG3fr4yUKwB8fzkrH9R8bQ,1064
46
+ fastled-1.4.10.dist-info/METADATA,sha256=ZUfEDRHSSsMmIZSZ4j3FCypY9lIZ4Yxuloo4HfU9QIc,31910
47
+ fastled-1.4.10.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
48
+ fastled-1.4.10.dist-info/entry_points.txt,sha256=RCwmzCSOS4-C2i9EziANq7Z2Zb4KFnEMR1FQC0bBwAw,101
49
+ fastled-1.4.10.dist-info/top_level.txt,sha256=Bbv5kpJpZhWNCvDF4K0VcvtBSDMa8B7PTOrZa9CezHY,8
50
+ fastled-1.4.10.dist-info/RECORD,,