fastled 1.1.34__py2.py3-none-any.whl → 1.1.36__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- fastled/__init__.py +68 -1
- fastled/app.py +6 -3
- fastled/client_server.py +3 -3
- fastled/compile_server.py +55 -166
- fastled/compile_server_impl.py +244 -0
- fastled/docker_manager.py +1 -1
- fastled/parse_args.py +2 -1
- fastled/project_init.py +10 -5
- fastled/{env.py → settings.py} +1 -0
- fastled/sketch.py +21 -0
- fastled/test/examples.py +16 -0
- fastled/types.py +15 -0
- fastled/web_compile.py +5 -13
- {fastled-1.1.34.dist-info → fastled-1.1.36.dist-info}/METADATA +3 -1
- fastled-1.1.36.dist-info/RECORD +30 -0
- {fastled-1.1.34.dist-info → fastled-1.1.36.dist-info}/top_level.txt +0 -1
- fastled-1.1.34.dist-info/RECORD +0 -28
- {fastled-1.1.34.dist-info → fastled-1.1.36.dist-info}/LICENSE +0 -0
- {fastled-1.1.34.dist-info → fastled-1.1.36.dist-info}/WHEEL +0 -0
- {fastled-1.1.34.dist-info → fastled-1.1.36.dist-info}/entry_points.txt +0 -0
fastled/__init__.py
CHANGED
@@ -1,3 +1,70 @@
|
|
1
1
|
"""FastLED Wasm Compiler package."""
|
2
2
|
|
3
|
-
|
3
|
+
from pathlib import Path
|
4
|
+
|
5
|
+
from .compile_server import CompileServer
|
6
|
+
from .types import WebCompileResult
|
7
|
+
|
8
|
+
__version__ = "1.1.36"
|
9
|
+
|
10
|
+
|
11
|
+
class Api:
|
12
|
+
@staticmethod
|
13
|
+
def get_examples():
|
14
|
+
from fastled.project_init import get_examples
|
15
|
+
|
16
|
+
return get_examples()
|
17
|
+
|
18
|
+
@staticmethod
|
19
|
+
def project_init(
|
20
|
+
example=None, outputdir=None, host: str | CompileServer | None = None
|
21
|
+
) -> Path:
|
22
|
+
from fastled.project_init import project_init
|
23
|
+
|
24
|
+
if isinstance(host, CompileServer):
|
25
|
+
host = host.url()
|
26
|
+
return project_init(example, outputdir, host)
|
27
|
+
|
28
|
+
@staticmethod
|
29
|
+
def web_compile(
|
30
|
+
directory, host: str | CompileServer | None = None
|
31
|
+
) -> WebCompileResult:
|
32
|
+
from fastled.web_compile import web_compile
|
33
|
+
|
34
|
+
if isinstance(host, CompileServer):
|
35
|
+
host = host.url()
|
36
|
+
|
37
|
+
out: WebCompileResult = web_compile(directory, host)
|
38
|
+
return out
|
39
|
+
|
40
|
+
@staticmethod
|
41
|
+
def spawn_server(
|
42
|
+
sketch_directory: Path | None = None,
|
43
|
+
interactive=False,
|
44
|
+
auto_updates=None,
|
45
|
+
auto_start=True,
|
46
|
+
container_name: str | None = None,
|
47
|
+
):
|
48
|
+
from fastled.compile_server import CompileServer
|
49
|
+
|
50
|
+
out = CompileServer(
|
51
|
+
container_name=container_name,
|
52
|
+
interactive=interactive,
|
53
|
+
auto_updates=auto_updates,
|
54
|
+
mapped_dir=sketch_directory,
|
55
|
+
auto_start=auto_start,
|
56
|
+
)
|
57
|
+
return out
|
58
|
+
|
59
|
+
|
60
|
+
class Test:
|
61
|
+
@staticmethod
|
62
|
+
def test_examples(
|
63
|
+
examples: list[str] | None = None, host: str | CompileServer | None = None
|
64
|
+
) -> Exception | None:
|
65
|
+
from fastled.test.examples import test_examples
|
66
|
+
|
67
|
+
if isinstance(host, CompileServer):
|
68
|
+
host = host.url()
|
69
|
+
|
70
|
+
return test_examples(examples=examples, host=host)
|
fastled/app.py
CHANGED
@@ -17,14 +17,17 @@ def run_server(args: argparse.Namespace) -> int:
|
|
17
17
|
auto_update = args.auto_update
|
18
18
|
mapped_dir = Path(args.directory).absolute() if args.directory else None
|
19
19
|
compile_server = CompileServer(
|
20
|
-
interactive=interactive,
|
20
|
+
interactive=interactive,
|
21
|
+
auto_updates=auto_update,
|
22
|
+
mapped_dir=mapped_dir,
|
23
|
+
auto_start=True,
|
21
24
|
)
|
25
|
+
|
22
26
|
if not interactive:
|
23
27
|
print(f"Server started at {compile_server.url()}")
|
24
|
-
compile_server.wait_for_startup()
|
25
28
|
try:
|
26
29
|
while True:
|
27
|
-
if not compile_server.
|
30
|
+
if not compile_server.process_running():
|
28
31
|
print("Server process is not running. Exiting...")
|
29
32
|
return 1
|
30
33
|
time.sleep(0.1)
|
fastled/client_server.py
CHANGED
@@ -8,10 +8,10 @@ from pathlib import Path
|
|
8
8
|
from fastled.build_mode import BuildMode, get_build_mode
|
9
9
|
from fastled.compile_server import CompileServer
|
10
10
|
from fastled.docker_manager import DockerManager
|
11
|
-
from fastled.env import DEFAULT_URL
|
12
11
|
from fastled.filewatcher import FileWatcherProcess
|
13
12
|
from fastled.keyboard import SpaceBarWatcher
|
14
13
|
from fastled.open_browser import open_browser_process
|
14
|
+
from fastled.settings import DEFAULT_URL
|
15
15
|
from fastled.sketch import looks_like_sketch_directory
|
16
16
|
|
17
17
|
# CompiledResult
|
@@ -112,7 +112,7 @@ def _try_start_server_or_get_url(args: argparse.Namespace) -> str | CompileServe
|
|
112
112
|
print("No local server found, starting one...")
|
113
113
|
compile_server = CompileServer(auto_updates=auto_update)
|
114
114
|
print("Waiting for the local compiler to start...")
|
115
|
-
if not compile_server.
|
115
|
+
if not compile_server.ping():
|
116
116
|
print("Failed to start local compiler.")
|
117
117
|
raise RuntimeError("Failed to start local compiler.")
|
118
118
|
return compile_server
|
@@ -254,7 +254,7 @@ def run_client_server(args: argparse.Namespace) -> int:
|
|
254
254
|
if changed:
|
255
255
|
print_status()
|
256
256
|
continue
|
257
|
-
if compile_server and not compile_server.
|
257
|
+
if compile_server and not compile_server.process_running():
|
258
258
|
print("Server process is not running. Exiting...")
|
259
259
|
return 1
|
260
260
|
if source_code_watcher is not None:
|
fastled/compile_server.py
CHANGED
@@ -1,193 +1,82 @@
|
|
1
|
-
import subprocess
|
2
|
-
import time
|
3
|
-
from datetime import datetime, timezone
|
4
1
|
from pathlib import Path
|
5
2
|
|
6
|
-
import
|
7
|
-
|
8
|
-
from fastled.docker_manager import (
|
9
|
-
DISK_CACHE,
|
10
|
-
Container,
|
11
|
-
DockerManager,
|
12
|
-
RunningContainer,
|
13
|
-
)
|
14
|
-
from fastled.sketch import looks_like_fastled_repo
|
15
|
-
|
16
|
-
_IMAGE_NAME = "niteris/fastled-wasm"
|
17
|
-
_DEFAULT_CONTAINER_NAME = "fastled-wasm-compiler"
|
18
|
-
|
19
|
-
SERVER_PORT = 9021
|
20
|
-
|
21
|
-
SERVER_OPTIONS = ["--allow-shutdown", "--no-auto-update"]
|
3
|
+
from fastled.build_mode import BuildMode
|
4
|
+
from fastled.types import WebCompileResult
|
22
5
|
|
23
6
|
|
24
7
|
class CompileServer:
|
25
8
|
def __init__(
|
26
9
|
self,
|
27
|
-
container_name=_DEFAULT_CONTAINER_NAME,
|
28
10
|
interactive: bool = False,
|
29
11
|
auto_updates: bool | None = None,
|
30
12
|
mapped_dir: Path | None = None,
|
13
|
+
auto_start: bool = True,
|
14
|
+
container_name: str | None = None,
|
31
15
|
) -> None:
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
self.
|
47
|
-
|
48
|
-
|
49
|
-
self
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
16
|
+
from fastled.compile_server_impl import ( # avoid circular import
|
17
|
+
CompileServerImpl,
|
18
|
+
)
|
19
|
+
|
20
|
+
self.impl = CompileServerImpl(
|
21
|
+
container_name=container_name,
|
22
|
+
interactive=interactive,
|
23
|
+
auto_updates=auto_updates,
|
24
|
+
mapped_dir=mapped_dir,
|
25
|
+
auto_start=auto_start,
|
26
|
+
)
|
27
|
+
|
28
|
+
def start(self, wait_for_startup=True) -> None:
|
29
|
+
# from fastled.compile_server_impl import CompileServerImpl # avoid circular import
|
30
|
+
self.impl.start(wait_for_startup=wait_for_startup)
|
31
|
+
|
32
|
+
def web_compile(
|
33
|
+
self,
|
34
|
+
directory: Path | str,
|
35
|
+
build_mode: BuildMode = BuildMode.QUICK,
|
36
|
+
profile: bool = False,
|
37
|
+
) -> WebCompileResult:
|
38
|
+
return self.impl.web_compile(
|
39
|
+
directory=directory, build_mode=build_mode, profile=profile
|
40
|
+
)
|
41
|
+
|
42
|
+
def project_init(
|
43
|
+
self, example: str | None = None, outputdir: Path | None = None
|
44
|
+
) -> None:
|
45
|
+
from fastled.project_init import project_init # avoid circular import
|
46
|
+
|
47
|
+
project_init(example=example, outputdir=outputdir)
|
60
48
|
|
61
49
|
@property
|
62
50
|
def running(self) -> bool:
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
return False
|
69
|
-
return self.docker.is_container_running(self.container_name)
|
51
|
+
return self.impl.running
|
52
|
+
|
53
|
+
@property
|
54
|
+
def fastled_src_dir(self) -> Path | None:
|
55
|
+
return self.impl.fastled_src_dir
|
70
56
|
|
71
57
|
def using_fastled_src_dir_volume(self) -> bool:
|
72
|
-
return self.
|
58
|
+
return self.impl.using_fastled_src_dir_volume()
|
73
59
|
|
74
60
|
def port(self) -> int:
|
75
|
-
return self.
|
61
|
+
return self.impl.port()
|
76
62
|
|
77
63
|
def url(self) -> str:
|
78
|
-
return
|
64
|
+
return self.impl.url()
|
65
|
+
|
66
|
+
def ping(self) -> bool:
|
67
|
+
return self.impl.ping()
|
79
68
|
|
69
|
+
# by default this is automatically called by the constructor, unless
|
70
|
+
# auto_start is set to False.
|
80
71
|
def wait_for_startup(self, timeout: int = 100) -> bool:
|
81
72
|
"""Wait for the server to start up."""
|
82
|
-
|
83
|
-
while time.time() - start_time < timeout:
|
84
|
-
# ping the server to see if it's up
|
85
|
-
if not self._port:
|
86
|
-
return False
|
87
|
-
# use httpx to ping the server
|
88
|
-
# if successful, return True
|
89
|
-
try:
|
90
|
-
response = httpx.get(
|
91
|
-
f"http://localhost:{self._port}", follow_redirects=True
|
92
|
-
)
|
93
|
-
if response.status_code < 400:
|
94
|
-
return True
|
95
|
-
except KeyboardInterrupt:
|
96
|
-
raise
|
97
|
-
except Exception:
|
98
|
-
pass
|
99
|
-
time.sleep(0.1)
|
100
|
-
if not self.docker.is_container_running(self.container_name):
|
101
|
-
return False
|
102
|
-
return False
|
73
|
+
return self.impl.wait_for_startup(timeout=timeout)
|
103
74
|
|
104
75
|
def _start(self) -> int:
|
105
|
-
|
106
|
-
|
107
|
-
# Ensure Docker is running
|
108
|
-
if not self.docker.is_running():
|
109
|
-
if not self.docker.start():
|
110
|
-
print("Docker could not be started. Exiting.")
|
111
|
-
raise RuntimeError("Docker could not be started. Exiting.")
|
112
|
-
now = datetime.now(timezone.utc)
|
113
|
-
now_str = now.strftime("%Y-%m-%d")
|
114
|
-
|
115
|
-
upgrade = False
|
116
|
-
if self.auto_updates is None:
|
117
|
-
prev_date_str = DISK_CACHE.get("last-update")
|
118
|
-
if prev_date_str != now_str:
|
119
|
-
print("One day has passed, checking docker for updates")
|
120
|
-
upgrade = True
|
121
|
-
else:
|
122
|
-
upgrade = self.auto_updates
|
123
|
-
self.docker.validate_or_download_image(
|
124
|
-
image_name=_IMAGE_NAME, tag="main", upgrade=upgrade
|
125
|
-
)
|
126
|
-
DISK_CACHE.put("last-update", now_str)
|
127
|
-
|
128
|
-
print("Docker image now validated")
|
129
|
-
port = SERVER_PORT
|
130
|
-
if self.interactive:
|
131
|
-
server_command = ["/bin/bash"]
|
132
|
-
else:
|
133
|
-
server_command = ["python", "/js/run.py", "server"] + SERVER_OPTIONS
|
134
|
-
ports = {80: port}
|
135
|
-
volumes = None
|
136
|
-
if self.fastled_src_dir:
|
137
|
-
print(
|
138
|
-
f"Mounting FastLED source directory {self.fastled_src_dir} into container /host/fastled/src"
|
139
|
-
)
|
140
|
-
volumes = {
|
141
|
-
str(self.fastled_src_dir): {"bind": "/host/fastled/src", "mode": "ro"}
|
142
|
-
}
|
143
|
-
if self.interactive:
|
144
|
-
# add the mapped directory to the container
|
145
|
-
print(f"Mounting {self.mapped_dir} into container /mapped")
|
146
|
-
# volumes = {str(self.mapped_dir): {"bind": "/mapped", "mode": "rw"}}
|
147
|
-
# add it
|
148
|
-
assert self.mapped_dir is not None
|
149
|
-
dir_name = self.mapped_dir.name
|
150
|
-
if not volumes:
|
151
|
-
volumes = {}
|
152
|
-
volumes[str(self.mapped_dir)] = {
|
153
|
-
"bind": f"/mapped/{dir_name}",
|
154
|
-
"mode": "rw",
|
155
|
-
}
|
156
|
-
|
157
|
-
cmd_str = subprocess.list2cmdline(server_command)
|
158
|
-
if not self.interactive:
|
159
|
-
container: Container = self.docker.run_container_detached(
|
160
|
-
image_name=_IMAGE_NAME,
|
161
|
-
tag="main",
|
162
|
-
container_name=self.container_name,
|
163
|
-
command=cmd_str,
|
164
|
-
ports=ports,
|
165
|
-
volumes=volumes,
|
166
|
-
remove_previous=self.interactive,
|
167
|
-
)
|
168
|
-
self.running_container = self.docker.attach_and_run(container)
|
169
|
-
assert self.running_container is not None, "Container should be running"
|
170
|
-
print("Compile server starting")
|
171
|
-
return port
|
172
|
-
else:
|
173
|
-
self.docker.run_container_interactive(
|
174
|
-
image_name=_IMAGE_NAME,
|
175
|
-
tag="main",
|
176
|
-
container_name=self.container_name,
|
177
|
-
command=cmd_str,
|
178
|
-
ports=ports,
|
179
|
-
volumes=volumes,
|
180
|
-
)
|
181
|
-
|
182
|
-
print("Exiting interactive mode")
|
183
|
-
return port
|
184
|
-
|
185
|
-
def proceess_running(self) -> bool:
|
186
|
-
return self.docker.is_container_running(self.container_name)
|
76
|
+
return self.impl._start()
|
187
77
|
|
188
78
|
def stop(self) -> None:
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
self.
|
193
|
-
print("Compile server stopped")
|
79
|
+
return self.impl.stop()
|
80
|
+
|
81
|
+
def process_running(self) -> bool:
|
82
|
+
return self.impl.process_running()
|
@@ -0,0 +1,244 @@
|
|
1
|
+
import subprocess
|
2
|
+
import time
|
3
|
+
import warnings
|
4
|
+
from datetime import datetime, timezone
|
5
|
+
from pathlib import Path
|
6
|
+
|
7
|
+
import httpx
|
8
|
+
|
9
|
+
from fastled.build_mode import BuildMode
|
10
|
+
from fastled.docker_manager import (
|
11
|
+
DISK_CACHE,
|
12
|
+
Container,
|
13
|
+
DockerManager,
|
14
|
+
RunningContainer,
|
15
|
+
)
|
16
|
+
from fastled.settings import SERVER_PORT
|
17
|
+
from fastled.sketch import looks_like_fastled_repo
|
18
|
+
from fastled.types import WebCompileResult
|
19
|
+
|
20
|
+
_IMAGE_NAME = "niteris/fastled-wasm"
|
21
|
+
_DEFAULT_CONTAINER_NAME = "fastled-wasm-compiler"
|
22
|
+
|
23
|
+
|
24
|
+
SERVER_OPTIONS = ["--allow-shutdown", "--no-auto-update"]
|
25
|
+
|
26
|
+
|
27
|
+
class CompileServerImpl:
|
28
|
+
def __init__(
|
29
|
+
self,
|
30
|
+
interactive: bool = False,
|
31
|
+
auto_updates: bool | None = None,
|
32
|
+
mapped_dir: Path | None = None,
|
33
|
+
auto_start: bool = True,
|
34
|
+
container_name: str | None = None,
|
35
|
+
) -> None:
|
36
|
+
container_name = container_name or _DEFAULT_CONTAINER_NAME
|
37
|
+
if interactive and not mapped_dir:
|
38
|
+
raise ValueError(
|
39
|
+
"Interactive mode requires a mapped directory point to a sketch"
|
40
|
+
)
|
41
|
+
if not interactive and mapped_dir:
|
42
|
+
raise ValueError("Mapped directory is only used in interactive mode")
|
43
|
+
cwd = Path(".").resolve()
|
44
|
+
fastled_src_dir: Path | None = None
|
45
|
+
if looks_like_fastled_repo(cwd):
|
46
|
+
print(
|
47
|
+
"Looks like a FastLED repo, using it as the source directory and mapping it into the server."
|
48
|
+
)
|
49
|
+
fastled_src_dir = cwd / "src"
|
50
|
+
|
51
|
+
self.container_name = container_name
|
52
|
+
self.mapped_dir = mapped_dir
|
53
|
+
self.docker = DockerManager()
|
54
|
+
self.fastled_src_dir: Path | None = fastled_src_dir
|
55
|
+
self.interactive = interactive
|
56
|
+
self.running_container: RunningContainer | None = None
|
57
|
+
self.auto_updates = auto_updates
|
58
|
+
# self._port = self._start()
|
59
|
+
self._port = 0 # 0 until compile server is started
|
60
|
+
# fancy print
|
61
|
+
if not interactive:
|
62
|
+
msg = f"# FastLED Compile Server started at {self.url()} #"
|
63
|
+
print("\n" + "#" * len(msg))
|
64
|
+
print(msg)
|
65
|
+
print("#" * len(msg) + "\n")
|
66
|
+
if auto_start:
|
67
|
+
self.start()
|
68
|
+
|
69
|
+
def start(self, wait_for_startup=True) -> None:
|
70
|
+
if self._port != 0:
|
71
|
+
warnings.warn("Server has already been started")
|
72
|
+
self._port = self._start()
|
73
|
+
if wait_for_startup:
|
74
|
+
self.wait_for_startup()
|
75
|
+
|
76
|
+
def web_compile(
|
77
|
+
self,
|
78
|
+
directory: Path | str,
|
79
|
+
build_mode: BuildMode = BuildMode.QUICK,
|
80
|
+
profile: bool = False,
|
81
|
+
) -> WebCompileResult:
|
82
|
+
from fastled.web_compile import web_compile # avoid circular import
|
83
|
+
|
84
|
+
if not self._port:
|
85
|
+
raise RuntimeError("Server has not been started yet")
|
86
|
+
if not self.ping():
|
87
|
+
raise RuntimeError("Server is not running")
|
88
|
+
out: WebCompileResult = web_compile(
|
89
|
+
directory, host=self.url(), build_mode=build_mode, profile=profile
|
90
|
+
)
|
91
|
+
return out
|
92
|
+
|
93
|
+
def project_init(
|
94
|
+
self, example: str | None = None, outputdir: Path | None = None
|
95
|
+
) -> None:
|
96
|
+
from fastled.project_init import project_init # avoid circular import
|
97
|
+
|
98
|
+
project_init(example=example, outputdir=outputdir)
|
99
|
+
|
100
|
+
@property
|
101
|
+
def running(self) -> bool:
|
102
|
+
if not self._port:
|
103
|
+
return False
|
104
|
+
if not DockerManager.is_docker_installed():
|
105
|
+
return False
|
106
|
+
if not DockerManager.is_running():
|
107
|
+
return False
|
108
|
+
return self.docker.is_container_running(self.container_name)
|
109
|
+
|
110
|
+
def using_fastled_src_dir_volume(self) -> bool:
|
111
|
+
return self.fastled_src_dir is not None
|
112
|
+
|
113
|
+
def port(self) -> int:
|
114
|
+
if self._port == 0:
|
115
|
+
warnings.warn("Server has not been started yet")
|
116
|
+
return self._port
|
117
|
+
|
118
|
+
def url(self) -> str:
|
119
|
+
if self._port == 0:
|
120
|
+
warnings.warn("Server has not been started yet")
|
121
|
+
return f"http://localhost:{self._port}"
|
122
|
+
|
123
|
+
def ping(self) -> bool:
|
124
|
+
try:
|
125
|
+
response = httpx.get(
|
126
|
+
f"http://localhost:{self._port}", follow_redirects=True
|
127
|
+
)
|
128
|
+
if response.status_code < 400:
|
129
|
+
return True
|
130
|
+
except KeyboardInterrupt:
|
131
|
+
raise
|
132
|
+
except Exception:
|
133
|
+
pass
|
134
|
+
return False
|
135
|
+
|
136
|
+
# by default this is automatically called by the constructor, unless
|
137
|
+
# auto_start is set to False.
|
138
|
+
def wait_for_startup(self, timeout: int = 100) -> bool:
|
139
|
+
"""Wait for the server to start up."""
|
140
|
+
start_time = time.time()
|
141
|
+
while time.time() - start_time < timeout:
|
142
|
+
# ping the server to see if it's up
|
143
|
+
if not self._port:
|
144
|
+
return False
|
145
|
+
# use httpx to ping the server
|
146
|
+
# if successful, return True
|
147
|
+
if self.ping():
|
148
|
+
return True
|
149
|
+
time.sleep(0.1)
|
150
|
+
if not self.docker.is_container_running(self.container_name):
|
151
|
+
return False
|
152
|
+
return False
|
153
|
+
|
154
|
+
def _start(self) -> int:
|
155
|
+
print("Compiling server starting")
|
156
|
+
|
157
|
+
# Ensure Docker is running
|
158
|
+
if not self.docker.is_running():
|
159
|
+
if not self.docker.start():
|
160
|
+
print("Docker could not be started. Exiting.")
|
161
|
+
raise RuntimeError("Docker could not be started. Exiting.")
|
162
|
+
now = datetime.now(timezone.utc)
|
163
|
+
now_str = now.strftime("%Y-%m-%d")
|
164
|
+
|
165
|
+
upgrade = False
|
166
|
+
if self.auto_updates is None:
|
167
|
+
prev_date_str = DISK_CACHE.get("last-update")
|
168
|
+
if prev_date_str != now_str:
|
169
|
+
print("One day has passed, checking docker for updates")
|
170
|
+
upgrade = True
|
171
|
+
else:
|
172
|
+
upgrade = self.auto_updates
|
173
|
+
self.docker.validate_or_download_image(
|
174
|
+
image_name=_IMAGE_NAME, tag="main", upgrade=upgrade
|
175
|
+
)
|
176
|
+
DISK_CACHE.put("last-update", now_str)
|
177
|
+
|
178
|
+
print("Docker image now validated")
|
179
|
+
port = SERVER_PORT
|
180
|
+
if self.interactive:
|
181
|
+
server_command = ["/bin/bash"]
|
182
|
+
else:
|
183
|
+
server_command = ["python", "/js/run.py", "server"] + SERVER_OPTIONS
|
184
|
+
ports = {80: port}
|
185
|
+
volumes = None
|
186
|
+
if self.fastled_src_dir:
|
187
|
+
print(
|
188
|
+
f"Mounting FastLED source directory {self.fastled_src_dir} into container /host/fastled/src"
|
189
|
+
)
|
190
|
+
volumes = {
|
191
|
+
str(self.fastled_src_dir): {"bind": "/host/fastled/src", "mode": "ro"}
|
192
|
+
}
|
193
|
+
if self.interactive:
|
194
|
+
# add the mapped directory to the container
|
195
|
+
print(f"Mounting {self.mapped_dir} into container /mapped")
|
196
|
+
# volumes = {str(self.mapped_dir): {"bind": "/mapped", "mode": "rw"}}
|
197
|
+
# add it
|
198
|
+
assert self.mapped_dir is not None
|
199
|
+
dir_name = self.mapped_dir.name
|
200
|
+
if not volumes:
|
201
|
+
volumes = {}
|
202
|
+
volumes[str(self.mapped_dir)] = {
|
203
|
+
"bind": f"/mapped/{dir_name}",
|
204
|
+
"mode": "rw",
|
205
|
+
}
|
206
|
+
|
207
|
+
cmd_str = subprocess.list2cmdline(server_command)
|
208
|
+
if not self.interactive:
|
209
|
+
container: Container = self.docker.run_container_detached(
|
210
|
+
image_name=_IMAGE_NAME,
|
211
|
+
tag="main",
|
212
|
+
container_name=self.container_name,
|
213
|
+
command=cmd_str,
|
214
|
+
ports=ports,
|
215
|
+
volumes=volumes,
|
216
|
+
remove_previous=self.interactive,
|
217
|
+
)
|
218
|
+
self.running_container = self.docker.attach_and_run(container)
|
219
|
+
assert self.running_container is not None, "Container should be running"
|
220
|
+
print("Compile server starting")
|
221
|
+
return port
|
222
|
+
else:
|
223
|
+
self.docker.run_container_interactive(
|
224
|
+
image_name=_IMAGE_NAME,
|
225
|
+
tag="main",
|
226
|
+
container_name=self.container_name,
|
227
|
+
command=cmd_str,
|
228
|
+
ports=ports,
|
229
|
+
volumes=volumes,
|
230
|
+
)
|
231
|
+
|
232
|
+
print("Exiting interactive mode")
|
233
|
+
return port
|
234
|
+
|
235
|
+
def process_running(self) -> bool:
|
236
|
+
return self.docker.is_container_running(self.container_name)
|
237
|
+
|
238
|
+
def stop(self) -> None:
|
239
|
+
if self.running_container:
|
240
|
+
self.running_container.detach()
|
241
|
+
self.running_container = None
|
242
|
+
self.docker.suspend_container(self.container_name)
|
243
|
+
self._port = 0
|
244
|
+
print("Compile server stopped")
|
fastled/docker_manager.py
CHANGED
fastled/parse_args.py
CHANGED
@@ -5,9 +5,9 @@ from pathlib import Path
|
|
5
5
|
|
6
6
|
from fastled import __version__
|
7
7
|
from fastled.docker_manager import DockerManager
|
8
|
-
from fastled.env import DEFAULT_URL
|
9
8
|
from fastled.project_init import project_init
|
10
9
|
from fastled.select_sketch_directory import select_sketch_directory
|
10
|
+
from fastled.settings import DEFAULT_URL
|
11
11
|
from fastled.sketch import (
|
12
12
|
find_sketch_directories,
|
13
13
|
looks_like_fastled_repo,
|
@@ -150,6 +150,7 @@ def parse_args() -> argparse.Namespace:
|
|
150
150
|
if looks_like_sketch_directory(maybe_sketch_dir):
|
151
151
|
args.directory = str(maybe_sketch_dir)
|
152
152
|
else:
|
153
|
+
print("Searching for sketch directories...")
|
153
154
|
sketch_directories = find_sketch_directories(maybe_sketch_dir)
|
154
155
|
selected_dir = select_sketch_directory(
|
155
156
|
sketch_directories, cwd_is_fastled
|
fastled/project_init.py
CHANGED
@@ -3,7 +3,7 @@ from pathlib import Path
|
|
3
3
|
|
4
4
|
import httpx
|
5
5
|
|
6
|
-
from fastled.
|
6
|
+
from fastled.settings import DEFAULT_URL
|
7
7
|
|
8
8
|
ENDPOINT_PROJECT_INIT = f"{DEFAULT_URL}/project/init"
|
9
9
|
ENDPOINT_INFO = f"{DEFAULT_URL}/info"
|
@@ -33,13 +33,17 @@ def _prompt_for_example() -> str:
|
|
33
33
|
return answer
|
34
34
|
|
35
35
|
|
36
|
-
def project_init(
|
36
|
+
def project_init(
|
37
|
+
example: str | None = "PROMPT", # prompt for example
|
38
|
+
outputdir: Path | None = None,
|
39
|
+
host: str | None = None,
|
40
|
+
) -> Path:
|
37
41
|
"""
|
38
42
|
Initialize a new FastLED project.
|
39
43
|
"""
|
40
|
-
|
44
|
+
host = host or DEFAULT_URL
|
41
45
|
outputdir = outputdir or Path("fastled")
|
42
|
-
if example is None:
|
46
|
+
if example == "PROMPT" or example is None:
|
43
47
|
try:
|
44
48
|
example = _prompt_for_example()
|
45
49
|
except httpx.HTTPStatusError:
|
@@ -48,7 +52,8 @@ def project_init(example: str | None = None, outputdir: Path | None = None) -> P
|
|
48
52
|
)
|
49
53
|
example = DEFAULT_EXAMPLE
|
50
54
|
assert example is not None
|
51
|
-
|
55
|
+
endpoint_url = f"{host}/project/init/{example}"
|
56
|
+
response = httpx.get(endpoint_url, timeout=20)
|
52
57
|
response.raise_for_status()
|
53
58
|
content = response.content
|
54
59
|
tmpzip = outputdir / "fastled.zip"
|
fastled/{env.py → settings.py}
RENAMED
fastled/sketch.py
CHANGED
@@ -1,8 +1,11 @@
|
|
1
1
|
import os
|
2
2
|
from pathlib import Path
|
3
3
|
|
4
|
+
_MAX_FILES_SEARCH_LIMIT = 10000
|
5
|
+
|
4
6
|
|
5
7
|
def find_sketch_directories(directory: Path) -> list[Path]:
|
8
|
+
file_count = 0
|
6
9
|
sketch_directories: list[Path] = []
|
7
10
|
# search all the paths one level deep
|
8
11
|
for path in directory.iterdir():
|
@@ -10,6 +13,12 @@ def find_sketch_directories(directory: Path) -> list[Path]:
|
|
10
13
|
dir_name = path.name
|
11
14
|
if str(dir_name).startswith("."):
|
12
15
|
continue
|
16
|
+
file_count += 1
|
17
|
+
if file_count > _MAX_FILES_SEARCH_LIMIT:
|
18
|
+
print(
|
19
|
+
f"More than {_MAX_FILES_SEARCH_LIMIT} files found. Stopping search."
|
20
|
+
)
|
21
|
+
break
|
13
22
|
|
14
23
|
if looks_like_sketch_directory(path, quick=True):
|
15
24
|
sketch_directories.append(path)
|
@@ -24,6 +33,7 @@ def find_sketch_directories(directory: Path) -> list[Path]:
|
|
24
33
|
|
25
34
|
|
26
35
|
def get_sketch_files(directory: Path) -> list[Path]:
|
36
|
+
file_count = 0
|
27
37
|
files: list[Path] = []
|
28
38
|
for root, dirs, filenames in os.walk(directory):
|
29
39
|
# ignore hidden directories
|
@@ -32,10 +42,21 @@ def get_sketch_files(directory: Path) -> list[Path]:
|
|
32
42
|
dirs[:] = [d for d in dirs if "fastled_js" not in d]
|
33
43
|
# ignore hidden files
|
34
44
|
filenames = [f for f in filenames if not f.startswith(".")]
|
45
|
+
outer_break = False
|
35
46
|
for filename in filenames:
|
36
47
|
if "platformio.ini" in filename:
|
37
48
|
continue
|
49
|
+
file_count += 1
|
50
|
+
if file_count > _MAX_FILES_SEARCH_LIMIT:
|
51
|
+
print(
|
52
|
+
f"More than {_MAX_FILES_SEARCH_LIMIT} files found. Stopping search."
|
53
|
+
)
|
54
|
+
outer_break = True
|
55
|
+
break
|
38
56
|
files.append(Path(root) / filename)
|
57
|
+
if outer_break:
|
58
|
+
break
|
59
|
+
|
39
60
|
return files
|
40
61
|
|
41
62
|
|
fastled/test/examples.py
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
from fastled import WebCompileResult
|
2
|
+
|
3
|
+
|
4
|
+
def test_examples(
|
5
|
+
examples: list[str] | None = None, host: str | None = None
|
6
|
+
) -> Exception | None:
|
7
|
+
"""Test the examples in the given directory."""
|
8
|
+
from fastled import Api
|
9
|
+
|
10
|
+
examples = Api.get_examples() if examples is None else examples
|
11
|
+
for example in examples:
|
12
|
+
print(f"Compiling example: {example}")
|
13
|
+
out: WebCompileResult = Api.web_compile(example, host=host)
|
14
|
+
if not out.success:
|
15
|
+
return Exception(out.stdout)
|
16
|
+
return None
|
fastled/types.py
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
from dataclasses import dataclass
|
2
|
+
from typing import Any
|
2
3
|
|
3
4
|
|
4
5
|
@dataclass
|
@@ -8,3 +9,17 @@ class CompiledResult:
|
|
8
9
|
success: bool
|
9
10
|
fastled_js: str
|
10
11
|
hash_value: str | None
|
12
|
+
|
13
|
+
|
14
|
+
@dataclass
|
15
|
+
class WebCompileResult:
|
16
|
+
success: bool
|
17
|
+
stdout: str
|
18
|
+
hash_value: str | None
|
19
|
+
zip_bytes: bytes
|
20
|
+
|
21
|
+
def __bool__(self) -> bool:
|
22
|
+
return self.success
|
23
|
+
|
24
|
+
def to_dict(self) -> dict[str, Any]:
|
25
|
+
return self.__dict__.copy()
|
fastled/web_compile.py
CHANGED
@@ -12,8 +12,9 @@ from pathlib import Path
|
|
12
12
|
import httpx
|
13
13
|
|
14
14
|
from fastled.build_mode import BuildMode
|
15
|
-
from fastled.
|
15
|
+
from fastled.settings import SERVER_PORT
|
16
16
|
from fastled.sketch import get_sketch_files
|
17
|
+
from fastled.types import WebCompileResult
|
17
18
|
from fastled.util import hash_file
|
18
19
|
|
19
20
|
DEFAULT_HOST = "https://fastled.onrender.com"
|
@@ -31,17 +32,6 @@ class ConnectionResult:
|
|
31
32
|
ipv4: bool
|
32
33
|
|
33
34
|
|
34
|
-
@dataclass
|
35
|
-
class WebCompileResult:
|
36
|
-
success: bool
|
37
|
-
stdout: str
|
38
|
-
hash_value: str | None
|
39
|
-
zip_bytes: bytes
|
40
|
-
|
41
|
-
def __bool__(self) -> bool:
|
42
|
-
return self.success
|
43
|
-
|
44
|
-
|
45
35
|
def _sanitize_host(host: str) -> str:
|
46
36
|
if host.startswith("http"):
|
47
37
|
return host
|
@@ -165,12 +155,14 @@ def find_good_connection(
|
|
165
155
|
|
166
156
|
|
167
157
|
def web_compile(
|
168
|
-
directory: Path,
|
158
|
+
directory: Path | str,
|
169
159
|
host: str | None = None,
|
170
160
|
auth_token: str | None = None,
|
171
161
|
build_mode: BuildMode | None = None,
|
172
162
|
profile: bool = False,
|
173
163
|
) -> WebCompileResult:
|
164
|
+
if isinstance(directory, str):
|
165
|
+
directory = Path(directory)
|
174
166
|
host = _sanitize_host(host or DEFAULT_HOST)
|
175
167
|
build_mode = build_mode or BuildMode.QUICK
|
176
168
|
print("Compiling on", host)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: fastled
|
3
|
-
Version: 1.1.
|
3
|
+
Version: 1.1.36
|
4
4
|
Summary: FastLED Wasm Compiler
|
5
5
|
Home-page: https://github.com/zackees/fastled-wasm
|
6
6
|
Maintainer: Zachary Vorhies
|
@@ -163,6 +163,8 @@ A: A big chunk of space is being used by unnecessary javascript `emscripten` is
|
|
163
163
|
|
164
164
|
# Revisions
|
165
165
|
|
166
|
+
* 1.1.36 - We now have an api. `from fastled import Api` and `from fastled import Test` for testing.
|
167
|
+
* 1.1.35 - When searching for files cap the limit at a high amount to prevent hang.
|
166
168
|
* 1.1.34 - On windows check to make sure we are in linux container mode, if not try to switch and if that fails then use `--web` compiler.
|
167
169
|
* 1.1.33 - Auto updating frequency has been reduced from one hour to one day. To update immediatly use `--update`.
|
168
170
|
* 1.1.32 - `--init` now asks for which example you want, then tells you where the example was downloaded to. No longer auto-compiles.
|
@@ -0,0 +1,30 @@
|
|
1
|
+
fastled/__init__.py,sha256=CAu_kGMgCncOxL47XeHzp8Ahj-O2Le86k5XwknbHYbA,1840
|
2
|
+
fastled/app.py,sha256=3xg7oVD-UYnKPU8SAY-Cs5UnAYdwpdpuEFRR2N8P1Tg,1787
|
3
|
+
fastled/build_mode.py,sha256=joMwsV4K1y_LijT4gEAcjx69RZBoe_KmFmHZdPYbL_4,631
|
4
|
+
fastled/cli.py,sha256=CNR_pQR0sNVPNuv8e_nmm-0PI8sU-eUBUgnWgWkzW9c,237
|
5
|
+
fastled/client_server.py,sha256=rre250O1uCx6JFi_nn6fhKUncgKOw3F7BgN9O9wbCEM,11440
|
6
|
+
fastled/compile_server.py,sha256=QxkXLpj9q7BWhPbS2NPeFF4WRne-358o-8fg4VEDH1o,2427
|
7
|
+
fastled/compile_server_impl.py,sha256=cB5WZ9znNhCFe2HKl-p1ZonfYKeaJOUyV3uHfkQtTZk,8481
|
8
|
+
fastled/docker_manager.py,sha256=zBCFGk2P3_bS7_SUQ5j2lpsOS3RvIzXYkrJXC6xP69k,25383
|
9
|
+
fastled/filewatcher.py,sha256=LwEQJkqADsArZyY499RLAer6JjJyDwaQBcAvT7xmp3c,6708
|
10
|
+
fastled/keyboard.py,sha256=Zz_ggxOUTX2XQEy6K6kAoorVlUev4wEk9Awpvv9aStA,3241
|
11
|
+
fastled/open_browser.py,sha256=vzMBcpDNY0f-Bx9KmEILKDANZ6gvsywCVwn1FRhPXh4,1770
|
12
|
+
fastled/parse_args.py,sha256=37WsELphNEqGQgmjppZx6uMWE2E-dZ58zCKUl-3mr3Q,6150
|
13
|
+
fastled/paths.py,sha256=VsPmgu0lNSCFOoEC0BsTYzDygXqy15AHUfN-tTuzDZA,99
|
14
|
+
fastled/project_init.py,sha256=5Wy-HONa81Ws0KHyUTzjkYWAXbDm1y7X58whKJbV5tc,2084
|
15
|
+
fastled/select_sketch_directory.py,sha256=TZdCjl1D7YMKjodMTvDRurPcpAmN3x0TcJxffER2NfM,1314
|
16
|
+
fastled/settings.py,sha256=3eMKv0tLXgIQ0CFDboIp_l5_71rzIIyWg353YjnYJnc,323
|
17
|
+
fastled/sketch.py,sha256=483TrrIdZJfo1MIu5FkD-V5OGmOfHmsZ2f6VvNsJBJM,3299
|
18
|
+
fastled/spinner.py,sha256=VHxmvB92P0Z_zYxRajb5HiNmkHHvZ5dG7hKtZltzpcs,867
|
19
|
+
fastled/string_diff.py,sha256=UR1oRhg9lsPzAG4bn_MwJMCn0evP5AigkBiwLiI9fgA,1354
|
20
|
+
fastled/types.py,sha256=4uSSuOUhZptTreor6CXXGSk-FIMOdwHfdvFI-4Yx6AY,475
|
21
|
+
fastled/util.py,sha256=t4M3NFMhnCzfYbLvIyJi0RdFssZqbTN_vVIaej1WV-U,265
|
22
|
+
fastled/web_compile.py,sha256=0jXFeej-YhOxcjIau8Ru8qEu-ULlsFjaufbqKYUUXjQ,10312
|
23
|
+
fastled/assets/example.txt,sha256=lTBovRjiz0_TgtAtbA1C5hNi2ffbqnNPqkKg6UiKCT8,54
|
24
|
+
fastled/test/examples.py,sha256=kUM1aDK-cD9WhbFzNPMXt_-LYqHHVgV2rLiEnzLpFW0,522
|
25
|
+
fastled-1.1.36.dist-info/LICENSE,sha256=b6pOoifSXiUaz_lDS84vWlG3fr4yUKwB8fzkrH9R8bQ,1064
|
26
|
+
fastled-1.1.36.dist-info/METADATA,sha256=2OuHb_W1FUx8PRj91B2cqABJ3UqU0TkABsr_21nwaak,15254
|
27
|
+
fastled-1.1.36.dist-info/WHEEL,sha256=0VNUDWQJzfRahYI3neAhz2UVbRCtztpN5dPHAGvmGXc,109
|
28
|
+
fastled-1.1.36.dist-info/entry_points.txt,sha256=RCwmzCSOS4-C2i9EziANq7Z2Zb4KFnEMR1FQC0bBwAw,101
|
29
|
+
fastled-1.1.36.dist-info/top_level.txt,sha256=Bbv5kpJpZhWNCvDF4K0VcvtBSDMa8B7PTOrZa9CezHY,8
|
30
|
+
fastled-1.1.36.dist-info/RECORD,,
|
fastled-1.1.34.dist-info/RECORD
DELETED
@@ -1,28 +0,0 @@
|
|
1
|
-
fastled/__init__.py,sha256=TRIZQh3TjPKDDHL3ZVGeuIOaPljhjQmkhKhLO6W6U4k,61
|
2
|
-
fastled/app.py,sha256=Zw1RS72yyIZSDt1_08MDSzfY4YwLWzarwXM4nvy8GE4,1787
|
3
|
-
fastled/build_mode.py,sha256=joMwsV4K1y_LijT4gEAcjx69RZBoe_KmFmHZdPYbL_4,631
|
4
|
-
fastled/cli.py,sha256=CNR_pQR0sNVPNuv8e_nmm-0PI8sU-eUBUgnWgWkzW9c,237
|
5
|
-
fastled/client_server.py,sha256=IK_u71XbmOAKBX1ovHATsCmfUON8VVJJu-XtObTuYJI,11448
|
6
|
-
fastled/compile_server.py,sha256=QnvlomTbFG2OmFj4ChuOXa0yBhhOvR8Bp1iIFsD3hbU,6717
|
7
|
-
fastled/docker_manager.py,sha256=x_lMreCkcY4ywLqU0pedTug80eo578ssaGUPystoDIs,25381
|
8
|
-
fastled/env.py,sha256=8wctQwl5qE4CI8NBugHtgMmUfEfHZ869JX5lGdSOJxc,304
|
9
|
-
fastled/filewatcher.py,sha256=LwEQJkqADsArZyY499RLAer6JjJyDwaQBcAvT7xmp3c,6708
|
10
|
-
fastled/keyboard.py,sha256=Zz_ggxOUTX2XQEy6K6kAoorVlUev4wEk9Awpvv9aStA,3241
|
11
|
-
fastled/open_browser.py,sha256=vzMBcpDNY0f-Bx9KmEILKDANZ6gvsywCVwn1FRhPXh4,1770
|
12
|
-
fastled/parse_args.py,sha256=OZkVjRNlparNYR0F9Ic4ud2B0pMSn4fnmVwUY1Va89o,6084
|
13
|
-
fastled/paths.py,sha256=VsPmgu0lNSCFOoEC0BsTYzDygXqy15AHUfN-tTuzDZA,99
|
14
|
-
fastled/project_init.py,sha256=ndQICZ9rBtfEPOnucZ3cgRE67PUKc66CJYO4Tkwmt_M,1932
|
15
|
-
fastled/select_sketch_directory.py,sha256=TZdCjl1D7YMKjodMTvDRurPcpAmN3x0TcJxffER2NfM,1314
|
16
|
-
fastled/sketch.py,sha256=5nRjg281lMH8Bo9wKjbcpTQCfEP574ZCG-lukvFmyQ8,2656
|
17
|
-
fastled/spinner.py,sha256=VHxmvB92P0Z_zYxRajb5HiNmkHHvZ5dG7hKtZltzpcs,867
|
18
|
-
fastled/string_diff.py,sha256=UR1oRhg9lsPzAG4bn_MwJMCn0evP5AigkBiwLiI9fgA,1354
|
19
|
-
fastled/types.py,sha256=dDIsGHJkHNJ7B61wNp6X0JSLs_nrHiq7RlNqNWbwFec,194
|
20
|
-
fastled/util.py,sha256=t4M3NFMhnCzfYbLvIyJi0RdFssZqbTN_vVIaej1WV-U,265
|
21
|
-
fastled/web_compile.py,sha256=KuvKGdX6SSUUqC7YgX4T9SMSP5wdcPUhpg9-K9zPoTI,10378
|
22
|
-
fastled/assets/example.txt,sha256=lTBovRjiz0_TgtAtbA1C5hNi2ffbqnNPqkKg6UiKCT8,54
|
23
|
-
fastled-1.1.34.dist-info/LICENSE,sha256=b6pOoifSXiUaz_lDS84vWlG3fr4yUKwB8fzkrH9R8bQ,1064
|
24
|
-
fastled-1.1.34.dist-info/METADATA,sha256=G3dvar486uKa16kYTZvDdD75n6MtqybcAh3g_H_d4E0,15063
|
25
|
-
fastled-1.1.34.dist-info/WHEEL,sha256=0VNUDWQJzfRahYI3neAhz2UVbRCtztpN5dPHAGvmGXc,109
|
26
|
-
fastled-1.1.34.dist-info/entry_points.txt,sha256=RCwmzCSOS4-C2i9EziANq7Z2Zb4KFnEMR1FQC0bBwAw,101
|
27
|
-
fastled-1.1.34.dist-info/top_level.txt,sha256=xfG6Z_ol9V5YmBROkZq2QTRwjbS2ouCUxaTJsOwfkOo,14
|
28
|
-
fastled-1.1.34.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|