fastled 1.1.16__py2.py3-none-any.whl → 1.1.18__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 +1 -1
- fastled/app.py +35 -16
- fastled/compile_server.py +19 -17
- fastled/docker_manager.py +45 -46
- fastled/filewatcher.py +202 -196
- {fastled-1.1.16.dist-info → fastled-1.1.18.dist-info}/METADATA +3 -1
- {fastled-1.1.16.dist-info → fastled-1.1.18.dist-info}/RECORD +11 -11
- {fastled-1.1.16.dist-info → fastled-1.1.18.dist-info}/LICENSE +0 -0
- {fastled-1.1.16.dist-info → fastled-1.1.18.dist-info}/WHEEL +0 -0
- {fastled-1.1.16.dist-info → fastled-1.1.18.dist-info}/entry_points.txt +0 -0
- {fastled-1.1.16.dist-info → fastled-1.1.18.dist-info}/top_level.txt +0 -0
fastled/__init__.py
CHANGED
fastled/app.py
CHANGED
@@ -82,38 +82,56 @@ def parse_args() -> argparse.Namespace:
|
|
82
82
|
action="store_true",
|
83
83
|
help="Enable profiling for web compilation",
|
84
84
|
)
|
85
|
-
|
86
|
-
|
87
|
-
build_mode.add_argument(
|
88
|
-
"--quick",
|
85
|
+
parser.add_argument(
|
86
|
+
"--force-compile",
|
89
87
|
action="store_true",
|
90
|
-
|
91
|
-
help="Build in quick mode (default)",
|
88
|
+
help="Skips the test to see if the current directory is a valid FastLED sketch directory",
|
92
89
|
)
|
93
|
-
|
94
|
-
"--
|
90
|
+
parser.add_argument(
|
91
|
+
"--no-auto-updates",
|
92
|
+
action="store_true",
|
93
|
+
help="Disable automatic updates of the wasm compiler image when using docker.",
|
95
94
|
)
|
96
|
-
|
95
|
+
parser.add_argument(
|
96
|
+
"--update",
|
97
|
+
action="store_true",
|
98
|
+
help="Update the wasm compiler (if necessary) before running",
|
99
|
+
)
|
100
|
+
parser.add_argument(
|
97
101
|
"--localhost",
|
98
102
|
"--local",
|
103
|
+
"-l",
|
99
104
|
action="store_true",
|
100
105
|
help="Use localhost for web compilation from an instance of fastled --server, creating it if necessary",
|
101
106
|
)
|
102
|
-
|
107
|
+
parser.add_argument(
|
103
108
|
"--server",
|
109
|
+
"-s",
|
104
110
|
action="store_true",
|
105
111
|
help="Run the server in the current directory, volume mapping fastled if we are in the repo",
|
106
112
|
)
|
107
|
-
|
113
|
+
build_mode = parser.add_mutually_exclusive_group()
|
114
|
+
build_mode.add_argument("--debug", action="store_true", help="Build in debug mode")
|
108
115
|
build_mode.add_argument(
|
109
|
-
"--
|
116
|
+
"--quick",
|
110
117
|
action="store_true",
|
111
|
-
|
118
|
+
default=True,
|
119
|
+
help="Build in quick mode (default)",
|
120
|
+
)
|
121
|
+
build_mode.add_argument(
|
122
|
+
"--release", action="store_true", help="Build in release mode"
|
112
123
|
)
|
113
124
|
|
114
125
|
cwd_is_fastled = looks_like_fastled_repo(Path(os.getcwd()))
|
115
126
|
|
116
127
|
args = parser.parse_args()
|
128
|
+
if args.update:
|
129
|
+
args.auto_update = True
|
130
|
+
elif args.no_auto_updates:
|
131
|
+
args.auto_update = False
|
132
|
+
else:
|
133
|
+
args.auto_update = None
|
134
|
+
|
117
135
|
if not cwd_is_fastled and not args.localhost and not args.web and not args.server:
|
118
136
|
print(f"Using web compiler at {DEFAULT_URL}")
|
119
137
|
args.web = DEFAULT_URL
|
@@ -199,8 +217,8 @@ def run_web_compiler(
|
|
199
217
|
|
200
218
|
|
201
219
|
def _try_start_server_or_get_url(args: argparse.Namespace) -> str | CompileServer:
|
220
|
+
auto_update = args.auto_update
|
202
221
|
is_local_host = "localhost" in args.web or "127.0.0.1" in args.web or args.localhost
|
203
|
-
|
204
222
|
# test to see if there is already a local host server
|
205
223
|
local_host_needs_server = False
|
206
224
|
if is_local_host:
|
@@ -225,7 +243,7 @@ def _try_start_server_or_get_url(args: argparse.Namespace) -> str | CompileServe
|
|
225
243
|
else:
|
226
244
|
try:
|
227
245
|
print("No local server found, starting one...")
|
228
|
-
compile_server = CompileServer()
|
246
|
+
compile_server = CompileServer(auto_updates=auto_update)
|
229
247
|
print("Waiting for the local compiler to start...")
|
230
248
|
if not compile_server.wait_for_startup():
|
231
249
|
print("Failed to start local compiler.")
|
@@ -397,7 +415,8 @@ def run_client(args: argparse.Namespace) -> int:
|
|
397
415
|
|
398
416
|
def run_server(args: argparse.Namespace) -> int:
|
399
417
|
interactive = args.interactive
|
400
|
-
|
418
|
+
auto_update = args.auto_update
|
419
|
+
compile_server = CompileServer(interactive=interactive, auto_updates=auto_update)
|
401
420
|
if not interactive:
|
402
421
|
print(f"Server started at {compile_server.url()}")
|
403
422
|
compile_server.wait_for_startup()
|
fastled/compile_server.py
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
import subprocess
|
2
2
|
import time
|
3
|
+
from datetime import datetime, timezone
|
3
4
|
from pathlib import Path
|
4
5
|
|
5
6
|
import httpx
|
@@ -20,6 +21,7 @@ class CompileServer:
|
|
20
21
|
self,
|
21
22
|
container_name=_DEFAULT_CONTAINER_NAME,
|
22
23
|
interactive: bool = False,
|
24
|
+
auto_updates: bool | None = None,
|
23
25
|
) -> None:
|
24
26
|
|
25
27
|
cwd = Path(".").resolve()
|
@@ -35,6 +37,7 @@ class CompileServer:
|
|
35
37
|
self.fastled_src_dir: Path | None = fastled_src_dir
|
36
38
|
self.interactive = interactive
|
37
39
|
self.running_container: RunningContainer | None = None
|
40
|
+
self.auto_updates = auto_updates
|
38
41
|
self._port = self._start()
|
39
42
|
# fancy print
|
40
43
|
if not interactive:
|
@@ -90,26 +93,25 @@ class CompileServer:
|
|
90
93
|
print("Compiling server starting")
|
91
94
|
|
92
95
|
# Ensure Docker is running
|
93
|
-
|
94
|
-
if not self.docker.
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
96
|
+
if not self.docker.is_running():
|
97
|
+
if not self.docker.start():
|
98
|
+
print("Docker could not be started. Exiting.")
|
99
|
+
raise RuntimeError("Docker could not be started. Exiting.")
|
100
|
+
now = datetime.now(timezone.utc)
|
101
|
+
now_str = now.strftime("%Y-%m-%d %H %Z")
|
102
|
+
|
103
|
+
upgrade = False
|
104
|
+
if self.auto_updates is None:
|
102
105
|
prev_date_str = DISK_CACHE.get("last-update")
|
103
|
-
|
104
|
-
upgrade = False
|
105
106
|
if prev_date_str != now_str:
|
106
|
-
print("
|
107
|
+
print("One hour has passed, checking docker for updates")
|
107
108
|
upgrade = True
|
108
|
-
|
109
|
-
self.
|
110
|
-
|
111
|
-
|
112
|
-
|
109
|
+
else:
|
110
|
+
upgrade = self.auto_updates
|
111
|
+
self.docker.validate_or_download_image(
|
112
|
+
image_name=_IMAGE_NAME, tag="main", upgrade=upgrade
|
113
|
+
)
|
114
|
+
DISK_CACHE.put("last-update", now_str)
|
113
115
|
|
114
116
|
print("Docker image now validated")
|
115
117
|
port = SERVER_PORT
|
fastled/docker_manager.py
CHANGED
@@ -45,8 +45,10 @@ def _win32_docker_location() -> str | None:
|
|
45
45
|
return None
|
46
46
|
|
47
47
|
|
48
|
-
|
49
|
-
|
48
|
+
def get_lock(image_name: str) -> FileLock:
|
49
|
+
"""Get the file lock for this DockerManager instance."""
|
50
|
+
lock_file = CONFIG_DIR / f"{image_name}.lock"
|
51
|
+
return FileLock(str(lock_file))
|
50
52
|
|
51
53
|
|
52
54
|
class RunningContainer:
|
@@ -90,10 +92,6 @@ class DockerManager:
|
|
90
92
|
self.client: DockerClient = docker.from_env()
|
91
93
|
self.first_run = False
|
92
94
|
|
93
|
-
def get_lock(self) -> FileLock:
|
94
|
-
"""Get the file lock for this DockerManager instance."""
|
95
|
-
return _FILE_LOCK
|
96
|
-
|
97
95
|
@staticmethod
|
98
96
|
def is_docker_installed() -> bool:
|
99
97
|
"""Check if Docker is installed on the system."""
|
@@ -178,53 +176,54 @@ class DockerManager:
|
|
178
176
|
If upgrade is True, will pull the latest version even if image exists locally.
|
179
177
|
"""
|
180
178
|
print(f"Validating image {image_name}:{tag}...")
|
179
|
+
remote_image_hash_from_local_image: str | None = None
|
180
|
+
remote_image_hash: str | None = None
|
181
181
|
|
182
|
-
|
183
|
-
|
184
|
-
|
182
|
+
with get_lock(f"{image_name}-{tag}"):
|
183
|
+
try:
|
184
|
+
local_image = self.client.images.get(f"{image_name}:{tag}")
|
185
|
+
print(f"Image {image_name}:{tag} is already available.")
|
185
186
|
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
)
|
190
|
-
remote_image_hash = remote_image.id
|
191
|
-
try:
|
192
|
-
remote_image_hash_from_local_image = DISK_CACHE.get(local_image.id)
|
193
|
-
except KeyboardInterrupt:
|
194
|
-
raise
|
195
|
-
except Exception:
|
196
|
-
remote_image_hash_from_local_image = None
|
197
|
-
import traceback
|
198
|
-
import warnings
|
199
|
-
|
200
|
-
stack = traceback.format_exc()
|
201
|
-
warnings.warn(
|
202
|
-
f"Error getting remote image hash from local image: {stack}"
|
187
|
+
if upgrade:
|
188
|
+
remote_image = self.client.images.get_registry_data(
|
189
|
+
f"{image_name}:{tag}"
|
203
190
|
)
|
204
|
-
|
205
|
-
print(f"Local image {image_name}:{tag} is up to date.")
|
206
|
-
return
|
191
|
+
remote_image_hash = remote_image.id
|
207
192
|
|
208
|
-
|
193
|
+
try:
|
194
|
+
remote_image_hash_from_local_image = DISK_CACHE.get(
|
195
|
+
local_image.id
|
196
|
+
)
|
197
|
+
except KeyboardInterrupt:
|
198
|
+
raise
|
199
|
+
except Exception:
|
200
|
+
remote_image_hash_from_local_image = None
|
201
|
+
stack = traceback.format_exc()
|
202
|
+
warnings.warn(
|
203
|
+
f"Error getting remote image hash from local image: {stack}"
|
204
|
+
)
|
205
|
+
if remote_image_hash_from_local_image == remote_image_hash:
|
206
|
+
print(f"Local image {image_name}:{tag} is up to date.")
|
207
|
+
return
|
209
208
|
|
210
|
-
|
211
|
-
_ = self.client.images.pull(image_name, tag=tag)
|
212
|
-
print(f"Updated to newer version of {image_name}:{tag}")
|
213
|
-
local_image_hash = self.client.images.get(f"{image_name}:{tag}").id
|
214
|
-
DISK_CACHE.put(local_image_hash, remote_image_hash)
|
209
|
+
# Quick check for latest version
|
215
210
|
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
DISK_CACHE.put(local_image_hash, remote_image_hash)
|
223
|
-
print(f"Image {image_name}:{tag} downloaded successfully.")
|
224
|
-
except docker.errors.ImageNotFound:
|
225
|
-
import warnings
|
211
|
+
print(f"Pulling newer version of {image_name}:{tag}...")
|
212
|
+
_ = self.client.images.pull(image_name, tag=tag)
|
213
|
+
print(f"Updated to newer version of {image_name}:{tag}")
|
214
|
+
local_image_hash = self.client.images.get(f"{image_name}:{tag}").id
|
215
|
+
if remote_image_hash is not None:
|
216
|
+
DISK_CACHE.put(local_image_hash, remote_image_hash)
|
226
217
|
|
227
|
-
|
218
|
+
except docker.errors.ImageNotFound:
|
219
|
+
print(f"Image {image_name}:{tag} not found. Downloading...")
|
220
|
+
self.client.images.pull(image_name, tag=tag)
|
221
|
+
try:
|
222
|
+
local_image = self.client.images.get(f"{image_name}:{tag}")
|
223
|
+
local_image_hash = local_image.id
|
224
|
+
print(f"Image {image_name}:{tag} downloaded successfully.")
|
225
|
+
except docker.errors.ImageNotFound:
|
226
|
+
warnings.warn(f"Image {image_name}:{tag} not found after download.")
|
228
227
|
|
229
228
|
def tag_image(self, image_name: str, old_tag: str, new_tag: str) -> None:
|
230
229
|
"""
|
fastled/filewatcher.py
CHANGED
@@ -1,196 +1,202 @@
|
|
1
|
-
"""File system watcher implementation using watchdog
|
2
|
-
"""
|
3
|
-
|
4
|
-
import hashlib
|
5
|
-
import os
|
6
|
-
import queue
|
7
|
-
import threading
|
8
|
-
import time
|
9
|
-
from contextlib import redirect_stdout
|
10
|
-
from multiprocessing import Process, Queue
|
11
|
-
from pathlib import Path
|
12
|
-
from queue import Empty
|
13
|
-
from typing import Dict, Set
|
14
|
-
|
15
|
-
from watchdog.events import FileSystemEvent, FileSystemEventHandler
|
16
|
-
from watchdog.observers import Observer
|
17
|
-
from watchdog.observers.api import BaseObserver
|
18
|
-
|
19
|
-
|
20
|
-
class MyEventHandler(FileSystemEventHandler):
|
21
|
-
def __init__(
|
22
|
-
self,
|
23
|
-
change_queue: queue.Queue,
|
24
|
-
excluded_patterns: Set[str],
|
25
|
-
file_hashes: Dict[str, str],
|
26
|
-
) -> None:
|
27
|
-
super().__init__()
|
28
|
-
self.change_queue = change_queue
|
29
|
-
self.excluded_patterns = excluded_patterns
|
30
|
-
self.file_hashes = file_hashes
|
31
|
-
|
32
|
-
def _get_file_hash(self, filepath: str) -> str:
|
33
|
-
try:
|
34
|
-
with open(filepath, "rb") as f:
|
35
|
-
return hashlib.md5(f.read()).hexdigest()
|
36
|
-
except Exception: # pylint: disable=broad-except
|
37
|
-
return ""
|
38
|
-
|
39
|
-
def on_modified(self, event: FileSystemEvent) -> None:
|
40
|
-
if not event.is_directory:
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
"""
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
self.
|
76
|
-
|
77
|
-
|
78
|
-
self.
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
self.
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
self.
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
self.
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
self.
|
185
|
-
|
186
|
-
def
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
1
|
+
"""File system watcher implementation using watchdog
|
2
|
+
"""
|
3
|
+
|
4
|
+
import hashlib
|
5
|
+
import os
|
6
|
+
import queue
|
7
|
+
import threading
|
8
|
+
import time
|
9
|
+
from contextlib import redirect_stdout
|
10
|
+
from multiprocessing import Process, Queue
|
11
|
+
from pathlib import Path
|
12
|
+
from queue import Empty
|
13
|
+
from typing import Dict, Set
|
14
|
+
|
15
|
+
from watchdog.events import FileSystemEvent, FileSystemEventHandler
|
16
|
+
from watchdog.observers import Observer
|
17
|
+
from watchdog.observers.api import BaseObserver
|
18
|
+
|
19
|
+
|
20
|
+
class MyEventHandler(FileSystemEventHandler):
|
21
|
+
def __init__(
|
22
|
+
self,
|
23
|
+
change_queue: queue.Queue,
|
24
|
+
excluded_patterns: Set[str],
|
25
|
+
file_hashes: Dict[str, str],
|
26
|
+
) -> None:
|
27
|
+
super().__init__()
|
28
|
+
self.change_queue = change_queue
|
29
|
+
self.excluded_patterns = excluded_patterns
|
30
|
+
self.file_hashes = file_hashes
|
31
|
+
|
32
|
+
def _get_file_hash(self, filepath: str) -> str:
|
33
|
+
try:
|
34
|
+
with open(filepath, "rb") as f:
|
35
|
+
return hashlib.md5(f.read()).hexdigest()
|
36
|
+
except Exception: # pylint: disable=broad-except
|
37
|
+
return ""
|
38
|
+
|
39
|
+
def on_modified(self, event: FileSystemEvent) -> None:
|
40
|
+
if not event.is_directory:
|
41
|
+
# Convert src_path to str if it's bytes
|
42
|
+
src_path = (
|
43
|
+
event.src_path.decode()
|
44
|
+
if isinstance(event.src_path, bytes)
|
45
|
+
else event.src_path
|
46
|
+
)
|
47
|
+
path = Path(src_path)
|
48
|
+
# Check if any part of the path matches excluded patterns
|
49
|
+
if not any(part in self.excluded_patterns for part in path.parts):
|
50
|
+
new_hash = self._get_file_hash(src_path)
|
51
|
+
if new_hash and new_hash != self.file_hashes.get(src_path):
|
52
|
+
self.file_hashes[src_path] = new_hash
|
53
|
+
self.change_queue.put(src_path)
|
54
|
+
|
55
|
+
|
56
|
+
class FileChangedNotifier(threading.Thread):
|
57
|
+
"""Watches a directory for file changes and queues notifications"""
|
58
|
+
|
59
|
+
def __init__(
|
60
|
+
self,
|
61
|
+
path: str,
|
62
|
+
debounce_seconds: float = 1.0,
|
63
|
+
excluded_patterns: list[str] | None = None,
|
64
|
+
) -> None:
|
65
|
+
"""Initialize the notifier with a path to watch
|
66
|
+
|
67
|
+
Args:
|
68
|
+
path: Directory path to watch for changes
|
69
|
+
debounce_seconds: Minimum time between notifications for the same file
|
70
|
+
excluded_patterns: List of directory/file patterns to exclude from watching
|
71
|
+
"""
|
72
|
+
super().__init__(daemon=True)
|
73
|
+
self.path = path
|
74
|
+
self.observer: BaseObserver | None = None
|
75
|
+
self.event_handler: MyEventHandler | None = None
|
76
|
+
|
77
|
+
# Combine default and user-provided patterns
|
78
|
+
self.excluded_patterns = (
|
79
|
+
set(excluded_patterns) if excluded_patterns is not None else set()
|
80
|
+
)
|
81
|
+
self.stopped = False
|
82
|
+
self.change_queue: queue.Queue = queue.Queue()
|
83
|
+
self.last_notification: Dict[str, float] = {}
|
84
|
+
self.file_hashes: Dict[str, str] = {}
|
85
|
+
self.debounce_seconds = debounce_seconds
|
86
|
+
|
87
|
+
def stop(self) -> None:
|
88
|
+
"""Stop watching for changes"""
|
89
|
+
print("watcher stop")
|
90
|
+
self.stopped = True
|
91
|
+
if self.observer:
|
92
|
+
self.observer.stop()
|
93
|
+
self.observer.join()
|
94
|
+
self.observer = None
|
95
|
+
self.event_handler = None
|
96
|
+
|
97
|
+
def run(self) -> None:
|
98
|
+
"""Thread main loop - starts watching for changes"""
|
99
|
+
self.event_handler = MyEventHandler(
|
100
|
+
self.change_queue, self.excluded_patterns, self.file_hashes
|
101
|
+
)
|
102
|
+
self.observer = Observer()
|
103
|
+
self.observer.schedule(self.event_handler, self.path, recursive=True)
|
104
|
+
self.observer.start()
|
105
|
+
|
106
|
+
try:
|
107
|
+
while not self.stopped:
|
108
|
+
time.sleep(0.1)
|
109
|
+
except KeyboardInterrupt:
|
110
|
+
print("File watcher stopped by user.")
|
111
|
+
finally:
|
112
|
+
self.stop()
|
113
|
+
|
114
|
+
def get_next_change(self, timeout: float = 0.001) -> str | None:
|
115
|
+
"""Get the next file change event from the queue
|
116
|
+
|
117
|
+
Args:
|
118
|
+
timeout: How long to wait for next change in seconds
|
119
|
+
|
120
|
+
Returns:
|
121
|
+
Changed filepath or None if no change within timeout
|
122
|
+
"""
|
123
|
+
try:
|
124
|
+
filepath = self.change_queue.get(timeout=timeout)
|
125
|
+
current_time = time.time()
|
126
|
+
|
127
|
+
# Check if we've seen this file recently
|
128
|
+
last_time = self.last_notification.get(filepath, 0)
|
129
|
+
if current_time - last_time < self.debounce_seconds:
|
130
|
+
return None
|
131
|
+
|
132
|
+
self.last_notification[filepath] = current_time
|
133
|
+
return filepath
|
134
|
+
except KeyboardInterrupt:
|
135
|
+
raise
|
136
|
+
except queue.Empty:
|
137
|
+
return None
|
138
|
+
|
139
|
+
def get_all_changes(self, timeout: float = 0.001) -> list[str]:
|
140
|
+
"""Get all file change events from the queue
|
141
|
+
|
142
|
+
Args:
|
143
|
+
timeout: How long to wait for next change in seconds
|
144
|
+
|
145
|
+
Returns:
|
146
|
+
List of changed filepaths
|
147
|
+
"""
|
148
|
+
changed_files = []
|
149
|
+
while True:
|
150
|
+
changed_file = self.get_next_change(timeout=timeout)
|
151
|
+
if changed_file is None:
|
152
|
+
break
|
153
|
+
changed_files.append(changed_file)
|
154
|
+
# clear all the changes from the queue
|
155
|
+
self.change_queue.queue.clear()
|
156
|
+
return changed_files
|
157
|
+
|
158
|
+
|
159
|
+
def _process_wrapper(root: Path, excluded_patterns: list[str], queue: Queue):
|
160
|
+
with open(os.devnull, "w") as fnull: # Redirect to /dev/null
|
161
|
+
with redirect_stdout(fnull):
|
162
|
+
watcher = FileChangedNotifier(
|
163
|
+
str(root), excluded_patterns=excluded_patterns
|
164
|
+
)
|
165
|
+
watcher.start()
|
166
|
+
while True:
|
167
|
+
try:
|
168
|
+
changed_files = watcher.get_all_changes()
|
169
|
+
for file in changed_files:
|
170
|
+
queue.put(file)
|
171
|
+
except KeyboardInterrupt:
|
172
|
+
break
|
173
|
+
watcher.stop()
|
174
|
+
|
175
|
+
|
176
|
+
class FileWatcherProcess:
|
177
|
+
def __init__(self, root: Path, excluded_patterns: list[str]) -> None:
|
178
|
+
self.queue: Queue = Queue()
|
179
|
+
self.process = Process(
|
180
|
+
target=_process_wrapper,
|
181
|
+
args=(root, excluded_patterns, self.queue),
|
182
|
+
daemon=True,
|
183
|
+
)
|
184
|
+
self.process.start()
|
185
|
+
|
186
|
+
def stop(self):
|
187
|
+
self.process.terminate()
|
188
|
+
self.process.join()
|
189
|
+
self.queue.close()
|
190
|
+
self.queue.join_thread()
|
191
|
+
|
192
|
+
def get_all_changes(self, timeout: float | None = None) -> list[str]:
|
193
|
+
changed_files = []
|
194
|
+
block = timeout is not None
|
195
|
+
|
196
|
+
while True:
|
197
|
+
try:
|
198
|
+
changed_file = self.queue.get(block=block, timeout=timeout)
|
199
|
+
changed_files.append(changed_file)
|
200
|
+
except Empty:
|
201
|
+
break
|
202
|
+
return changed_files
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: fastled
|
3
|
-
Version: 1.1.
|
3
|
+
Version: 1.1.18
|
4
4
|
Summary: FastLED Wasm Compiler
|
5
5
|
Home-page: https://github.com/zackees/fastled-wasm
|
6
6
|
Maintainer: Zachary Vorhies
|
@@ -161,6 +161,8 @@ A: A big chunk of space is being used by unnecessary javascript `emscripten` is
|
|
161
161
|
|
162
162
|
# Revisions
|
163
163
|
|
164
|
+
* 1.1.18 - Fixes for when the image has never been downloaded.
|
165
|
+
* 1.1.17 - Added `--update` and `--no-auto-update` to control whether the compiler in docker mode will try to update.
|
164
166
|
* 1.1.16 - Rewrote docker logic to use container suspension and resumption. Much much faster.
|
165
167
|
* 1.1.15 - Fixed logic for considering ipv6 addresses. Auto selection of ipv6 is now restored.
|
166
168
|
* 1.1.14 - Fixes for regression in using --server and --localhost as two instances, this is now under test.
|
@@ -1,10 +1,10 @@
|
|
1
|
-
fastled/__init__.py,sha256=
|
2
|
-
fastled/app.py,sha256=
|
1
|
+
fastled/__init__.py,sha256=IHbpe-aq0aVLbSOXEoXCoEu9sNWQTsV5OEghbd4DLsE,64
|
2
|
+
fastled/app.py,sha256=dUCHM-1yQGrpTtfUCn1NYlG7hIJm_Tg445zhyxsMBhY,15715
|
3
3
|
fastled/build_mode.py,sha256=joMwsV4K1y_LijT4gEAcjx69RZBoe_KmFmHZdPYbL_4,631
|
4
4
|
fastled/cli.py,sha256=CNR_pQR0sNVPNuv8e_nmm-0PI8sU-eUBUgnWgWkzW9c,237
|
5
|
-
fastled/compile_server.py,sha256=
|
6
|
-
fastled/docker_manager.py,sha256=
|
7
|
-
fastled/filewatcher.py,sha256=
|
5
|
+
fastled/compile_server.py,sha256=aBdpILSRrDsCJ5e9g5uwIqt9bcqE_8FrSddCV2ygtrI,5401
|
6
|
+
fastled/docker_manager.py,sha256=AzQBXqWB1Uq_Qb1mp1NS4hIbkC8aYp-pzpYhFRpI5ww,19940
|
7
|
+
fastled/filewatcher.py,sha256=5dVmjEG23kMeJa29tRVm5XKSr9sTD4ME2boo-CFDuUM,6910
|
8
8
|
fastled/keyboard.py,sha256=rqndglWYzRy6oiqHgsmx1peLd0Yrpci01zGENlCzh_s,2576
|
9
9
|
fastled/open_browser.py,sha256=RRHcsZ5Vzsw1AuZUEYuSfjKmf_9j3NGMDUR-FndHmqs,1483
|
10
10
|
fastled/paths.py,sha256=VsPmgu0lNSCFOoEC0BsTYzDygXqy15AHUfN-tTuzDZA,99
|
@@ -12,9 +12,9 @@ fastled/sketch.py,sha256=KhhPFqlFVlBk8YrzFy7-ioe7zEzecgrVLhyFbLpBp7k,1845
|
|
12
12
|
fastled/util.py,sha256=t4M3NFMhnCzfYbLvIyJi0RdFssZqbTN_vVIaej1WV-U,265
|
13
13
|
fastled/web_compile.py,sha256=KuvKGdX6SSUUqC7YgX4T9SMSP5wdcPUhpg9-K9zPoTI,10378
|
14
14
|
fastled/assets/example.txt,sha256=lTBovRjiz0_TgtAtbA1C5hNi2ffbqnNPqkKg6UiKCT8,54
|
15
|
-
fastled-1.1.
|
16
|
-
fastled-1.1.
|
17
|
-
fastled-1.1.
|
18
|
-
fastled-1.1.
|
19
|
-
fastled-1.1.
|
20
|
-
fastled-1.1.
|
15
|
+
fastled-1.1.18.dist-info/LICENSE,sha256=b6pOoifSXiUaz_lDS84vWlG3fr4yUKwB8fzkrH9R8bQ,1064
|
16
|
+
fastled-1.1.18.dist-info/METADATA,sha256=5crAxI6_10i2pfPFIX8KxFCmX_3yyvGWZKjBR6JIRMU,13562
|
17
|
+
fastled-1.1.18.dist-info/WHEEL,sha256=0VNUDWQJzfRahYI3neAhz2UVbRCtztpN5dPHAGvmGXc,109
|
18
|
+
fastled-1.1.18.dist-info/entry_points.txt,sha256=RCwmzCSOS4-C2i9EziANq7Z2Zb4KFnEMR1FQC0bBwAw,101
|
19
|
+
fastled-1.1.18.dist-info/top_level.txt,sha256=xfG6Z_ol9V5YmBROkZq2QTRwjbS2ouCUxaTJsOwfkOo,14
|
20
|
+
fastled-1.1.18.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|