fastled 1.2.46__py3-none-any.whl → 1.2.48__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/docker_manager.py CHANGED
@@ -18,6 +18,7 @@ import docker
18
18
  from appdirs import user_data_dir
19
19
  from disklru import DiskLRUCache
20
20
  from docker.client import DockerClient
21
+ from docker.errors import DockerException, NotFound
21
22
  from docker.models.containers import Container
22
23
  from docker.models.images import Image
23
24
  from filelock import FileLock
@@ -57,7 +58,9 @@ def get_lock(image_name: str) -> FileLock:
57
58
  print(CONFIG_DIR)
58
59
  if not lock_file.parent.exists():
59
60
  lock_file.parent.mkdir(parents=True, exist_ok=True)
60
- return FileLock(str(lock_file))
61
+ out: FileLock
62
+ out = FileLock(str(lock_file)) # type: ignore
63
+ return out
61
64
 
62
65
 
63
66
  class RunningContainer:
@@ -181,10 +184,6 @@ class DockerManager:
181
184
  )
182
185
  return False
183
186
 
184
- except subprocess.CalledProcessError as e:
185
- print(f"Error occurred: {e}")
186
- return False
187
-
188
187
  except subprocess.CalledProcessError as e:
189
188
  print(f"Failed to switch to Linux containers: {e}")
190
189
  if e.stdout:
@@ -199,6 +198,7 @@ class DockerManager:
199
198
  @staticmethod
200
199
  def is_running() -> bool:
201
200
  """Check if Docker is running by pinging the Docker daemon."""
201
+
202
202
  if not DockerManager.is_docker_installed():
203
203
  return False
204
204
  try:
@@ -207,7 +207,7 @@ class DockerManager:
207
207
  client.ping()
208
208
  print("Docker is running.")
209
209
  return True
210
- except docker.errors.DockerException as e:
210
+ except DockerException as e:
211
211
  print(f"Docker is not running: {str(e)}")
212
212
  return False
213
213
  except Exception as e:
@@ -289,8 +289,10 @@ class DockerManager:
289
289
  remote_image_hash = remote_image.id
290
290
 
291
291
  try:
292
+ local_image_id = local_image.id
293
+ assert local_image_id is not None
292
294
  remote_image_hash_from_local_image = DISK_CACHE.get(
293
- local_image.id
295
+ local_image_id
294
296
  )
295
297
  except KeyboardInterrupt:
296
298
  raise
@@ -455,14 +457,14 @@ class DockerManager:
455
457
  if remove_previous:
456
458
  print(f"Removing existing container {container_name}...")
457
459
  container.remove(force=True)
458
- raise docker.errors.NotFound("Container removed due to remove_previous")
460
+ raise NotFound("Container removed due to remove_previous")
459
461
  # Check if configuration matches
460
462
  elif not self._container_configs_match(container, command, volumes, ports):
461
463
  print(
462
464
  f"Container {container_name} exists but with different configuration. Removing and recreating..."
463
465
  )
464
466
  container.remove(force=True)
465
- raise docker.errors.NotFound("Container removed due to config mismatch")
467
+ raise NotFound("Container removed due to config mismatch")
466
468
  print(f"Container {container_name} found with matching configuration.")
467
469
 
468
470
  # Existing container with matching config - handle various states
@@ -492,7 +494,7 @@ class DockerManager:
492
494
  print(f"Starting existing container {container_name}.")
493
495
  self.first_run = True
494
496
  container.start()
495
- except docker.errors.NotFound:
497
+ except NotFound:
496
498
  print(f"Creating and starting {container_name}")
497
499
  out_msg = f"# Running in container: {command}"
498
500
  msg_len = len(out_msg)
@@ -524,7 +526,7 @@ class DockerManager:
524
526
  try:
525
527
  container: Container = self.client.containers.get(container_name)
526
528
  container.remove(force=True)
527
- except docker.errors.NotFound:
529
+ except NotFound:
528
530
  pass
529
531
  start_time = time.time()
530
532
  try:
@@ -615,7 +617,7 @@ class DockerManager:
615
617
  """
616
618
  try:
617
619
  return self.client.containers.get(container_name)
618
- except docker.errors.NotFound:
620
+ except NotFound:
619
621
  return None
620
622
 
621
623
  def is_container_running(self, container_name: str) -> bool:
@@ -625,7 +627,7 @@ class DockerManager:
625
627
  try:
626
628
  container = self.client.containers.get(container_name)
627
629
  return container.status == "running"
628
- except docker.errors.NotFound:
630
+ except NotFound:
629
631
  print(f"Container {container_name} not found.")
630
632
  return False
631
633
 
fastled/filewatcher.py CHANGED
@@ -15,6 +15,8 @@ from watchdog.events import FileSystemEvent, FileSystemEventHandler
15
15
  from watchdog.observers import Observer
16
16
  from watchdog.observers.api import BaseObserver
17
17
 
18
+ _WATCHER_TIMEOUT = 0.1
19
+
18
20
 
19
21
  def file_watcher_enabled() -> bool:
20
22
  """Check if watchdog is disabled"""
@@ -120,7 +122,7 @@ class FileChangedNotifier(threading.Thread):
120
122
  finally:
121
123
  self.stop()
122
124
 
123
- def get_next_change(self, timeout: float = 0.001) -> str | None:
125
+ def get_next_change(self, timeout: float = _WATCHER_TIMEOUT) -> str | None:
124
126
  """Get the next file change event from the queue
125
127
 
126
128
  Args:
@@ -148,7 +150,7 @@ class FileChangedNotifier(threading.Thread):
148
150
  except queue.Empty:
149
151
  return None
150
152
 
151
- def get_all_changes(self, timeout: float = 0.001) -> list[str]:
153
+ def get_all_changes(self, timeout: float = _WATCHER_TIMEOUT) -> list[str]:
152
154
  """Get all file change events from the queue
153
155
 
154
156
  Args:
@@ -185,12 +187,22 @@ def _process_wrapper(root: Path, excluded_patterns: list[str], queue: Queue):
185
187
  watcher.stop()
186
188
 
187
189
 
190
+ class ProcessWraperTask:
191
+ def __init__(self, root: Path, excluded_patterns: list[str], queue: Queue) -> None:
192
+ self.root = root
193
+ self.excluded_patterns = excluded_patterns
194
+ self.queue = queue
195
+
196
+ def run(self):
197
+ _process_wrapper(self.root, self.excluded_patterns, self.queue)
198
+
199
+
188
200
  class FileWatcherProcess:
189
201
  def __init__(self, root: Path, excluded_patterns: list[str]) -> None:
190
202
  self.queue: Queue = Queue()
203
+ task = ProcessWraperTask(root, excluded_patterns, self.queue)
191
204
  self.process = Process(
192
- target=_process_wrapper,
193
- args=(root, excluded_patterns, self.queue),
205
+ target=task.run,
194
206
  daemon=True,
195
207
  )
196
208
  self.process.start()
fastled/project_init.py CHANGED
@@ -1,129 +1,129 @@
1
- import _thread
2
- import threading
3
- import time
4
- import zipfile
5
- from pathlib import Path
6
-
7
- import httpx
8
-
9
- from fastled.settings import DEFAULT_URL
10
- from fastled.spinner import Spinner
11
-
12
- DEFAULT_EXAMPLE = "wasm"
13
-
14
-
15
- def get_examples(host: str | None = None) -> list[str]:
16
- host = host or DEFAULT_URL
17
- url_info = f"{host}/info"
18
- response = httpx.get(url_info, timeout=4)
19
- response.raise_for_status()
20
- examples: list[str] = response.json()["examples"]
21
- return sorted(examples)
22
-
23
-
24
- def _prompt_for_example() -> str:
25
- examples = get_examples()
26
- while True:
27
- print("Available examples:")
28
- for i, example in enumerate(examples):
29
- print(f" [{i+1}]: {example}")
30
- answer = input("Enter the example number or name: ").strip()
31
- if answer.isdigit():
32
- example_num = int(answer) - 1
33
- if example_num < 0 or example_num >= len(examples):
34
- print("Invalid example number")
35
- continue
36
- return examples[example_num]
37
- elif answer in examples:
38
- return answer
39
-
40
-
41
- class DownloadThread(threading.Thread):
42
- def __init__(self, url: str, json: str):
43
- super().__init__(daemon=True)
44
- self.url = url
45
- self.json = json
46
- self.bytes_downloaded = 0
47
- self.content: bytes | None = None
48
- self.error: Exception | None = None
49
- self.success = False
50
-
51
- def run(self) -> None:
52
- timeout = httpx.Timeout(5.0, connect=5.0, read=120.0, write=30.0)
53
- try:
54
- with httpx.Client(timeout=timeout) as client:
55
- with client.stream("POST", self.url, json=self.json) as response:
56
- response.raise_for_status()
57
- content = b""
58
- for chunk in response.iter_bytes():
59
- content += chunk
60
- self.bytes_downloaded += len(chunk)
61
- self.content = content
62
- self.success = True
63
- except KeyboardInterrupt:
64
- self.error = RuntimeError("Download cancelled")
65
- _thread.interrupt_main()
66
- except Exception as e:
67
- self.error = e
68
-
69
-
70
- def project_init(
71
- example: str | None = "PROMPT", # prompt for example
72
- outputdir: Path | None = None,
73
- host: str | None = None,
74
- ) -> Path:
75
- """
76
- Initialize a new FastLED project.
77
- """
78
- host = host or DEFAULT_URL
79
- outputdir = Path(outputdir) if outputdir is not None else Path("fastled")
80
- outputdir.mkdir(exist_ok=True, parents=True)
81
- if example == "PROMPT" or example is None:
82
- try:
83
- example = _prompt_for_example()
84
- except httpx.HTTPStatusError:
85
- print(
86
- f"Failed to fetch examples, using default example '{DEFAULT_EXAMPLE}'"
87
- )
88
- example = DEFAULT_EXAMPLE
89
- assert example is not None
90
- endpoint_url = f"{host}/project/init"
91
- json = example
92
- print(f"Initializing project with example '{example}', url={endpoint_url}")
93
-
94
- # Start download thread
95
- download_thread = DownloadThread(endpoint_url, json)
96
- # spinner = Spinner("Downloading project...")
97
- with Spinner(f"Downloading project {example}..."):
98
- download_thread.start()
99
- while download_thread.is_alive():
100
- time.sleep(0.1)
101
-
102
- print() # New line after progress
103
- download_thread.join()
104
-
105
- # Check for errors
106
- if not download_thread.success:
107
- assert download_thread.error is not None
108
- raise download_thread.error
109
-
110
- content = download_thread.content
111
- assert content is not None
112
- tmpzip = outputdir / "fastled.zip"
113
- outputdir.mkdir(exist_ok=True)
114
- tmpzip.write_bytes(content)
115
- with zipfile.ZipFile(tmpzip, "r") as zip_ref:
116
- zip_ref.extractall(outputdir)
117
- tmpzip.unlink()
118
- out = outputdir / example
119
- print(f"Project initialized at {out}")
120
- assert out.exists()
121
- return out
122
-
123
-
124
- def unit_test() -> None:
125
- project_init()
126
-
127
-
128
- if __name__ == "__main__":
129
- unit_test()
1
+ import _thread
2
+ import threading
3
+ import time
4
+ import zipfile
5
+ from pathlib import Path
6
+
7
+ import httpx
8
+
9
+ from fastled.settings import DEFAULT_URL
10
+ from fastled.spinner import Spinner
11
+
12
+ DEFAULT_EXAMPLE = "wasm"
13
+
14
+
15
+ def get_examples(host: str | None = None) -> list[str]:
16
+ host = host or DEFAULT_URL
17
+ url_info = f"{host}/info"
18
+ response = httpx.get(url_info, timeout=4)
19
+ response.raise_for_status()
20
+ examples: list[str] = response.json()["examples"]
21
+ return sorted(examples)
22
+
23
+
24
+ def _prompt_for_example() -> str:
25
+ examples = get_examples()
26
+ while True:
27
+ print("Available examples:")
28
+ for i, example in enumerate(examples):
29
+ print(f" [{i+1}]: {example}")
30
+ answer = input("Enter the example number or name: ").strip()
31
+ if answer.isdigit():
32
+ example_num = int(answer) - 1
33
+ if example_num < 0 or example_num >= len(examples):
34
+ print("Invalid example number")
35
+ continue
36
+ return examples[example_num]
37
+ elif answer in examples:
38
+ return answer
39
+
40
+
41
+ class DownloadThread(threading.Thread):
42
+ def __init__(self, url: str, json: str):
43
+ super().__init__(daemon=True)
44
+ self.url = url
45
+ self.json = json
46
+ self.bytes_downloaded = 0
47
+ self.content: bytes | None = None
48
+ self.error: Exception | None = None
49
+ self.success = False
50
+
51
+ def run(self) -> None:
52
+ timeout = httpx.Timeout(5.0, connect=5.0, read=120.0, write=30.0)
53
+ try:
54
+ with httpx.Client(timeout=timeout) as client:
55
+ with client.stream("POST", self.url, json=self.json) as response:
56
+ response.raise_for_status()
57
+ content = b""
58
+ for chunk in response.iter_bytes():
59
+ content += chunk
60
+ self.bytes_downloaded += len(chunk)
61
+ self.content = content
62
+ self.success = True
63
+ except KeyboardInterrupt:
64
+ self.error = RuntimeError("Download cancelled")
65
+ _thread.interrupt_main()
66
+ except Exception as e:
67
+ self.error = e
68
+
69
+
70
+ def project_init(
71
+ example: str | None = "PROMPT", # prompt for example
72
+ outputdir: Path | None = None,
73
+ host: str | None = None,
74
+ ) -> Path:
75
+ """
76
+ Initialize a new FastLED project.
77
+ """
78
+ host = host or DEFAULT_URL
79
+ outputdir = Path(outputdir) if outputdir is not None else Path("fastled")
80
+ outputdir.mkdir(exist_ok=True, parents=True)
81
+ if example == "PROMPT" or example is None:
82
+ try:
83
+ example = _prompt_for_example()
84
+ except httpx.HTTPStatusError:
85
+ print(
86
+ f"Failed to fetch examples, using default example '{DEFAULT_EXAMPLE}'"
87
+ )
88
+ example = DEFAULT_EXAMPLE
89
+ assert example is not None
90
+ endpoint_url = f"{host}/project/init"
91
+ json = example
92
+ print(f"Initializing project with example '{example}', url={endpoint_url}")
93
+
94
+ # Start download thread
95
+ download_thread = DownloadThread(endpoint_url, json)
96
+ # spinner = Spinner("Downloading project...")
97
+ with Spinner(f"Downloading project {example}..."):
98
+ download_thread.start()
99
+ while download_thread.is_alive():
100
+ time.sleep(0.1)
101
+
102
+ print() # New line after progress
103
+ download_thread.join()
104
+
105
+ # Check for errors
106
+ if not download_thread.success:
107
+ assert download_thread.error is not None
108
+ raise download_thread.error
109
+
110
+ content = download_thread.content
111
+ assert content is not None
112
+ tmpzip = outputdir / "fastled.zip"
113
+ outputdir.mkdir(exist_ok=True)
114
+ tmpzip.write_bytes(content)
115
+ with zipfile.ZipFile(tmpzip, "r") as zip_ref:
116
+ zip_ref.extractall(outputdir)
117
+ tmpzip.unlink()
118
+ out = outputdir / example
119
+ print(f"Project initialized at {out}")
120
+ assert out.exists()
121
+ return out
122
+
123
+
124
+ def unit_test() -> None:
125
+ project_init()
126
+
127
+
128
+ if __name__ == "__main__":
129
+ unit_test()