fastled 1.2.48__py3-none-any.whl → 1.2.49__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,7 +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
+ from docker.errors import DockerException, ImageNotFound, NotFound
22
22
  from docker.models.containers import Container
23
23
  from docker.models.images import Image
24
24
  from filelock import FileLock
@@ -98,6 +98,11 @@ class RunningContainer:
98
98
  self.running = False
99
99
  self.thread.join()
100
100
 
101
+ def stop(self) -> None:
102
+ """Stop the container"""
103
+ self.container.stop()
104
+ self.detach()
105
+
101
106
 
102
107
  class DockerManager:
103
108
  def __init__(self) -> None:
@@ -316,11 +321,12 @@ class DockerManager:
316
321
  subprocess.run(cmd_list, check=True)
317
322
  print(f"Updated to newer version of {image_name}:{tag}")
318
323
  local_image_hash = self.client.images.get(f"{image_name}:{tag}").id
324
+ assert local_image_hash is not None
319
325
  if remote_image_hash is not None:
320
326
  DISK_CACHE.put(local_image_hash, remote_image_hash)
321
327
  return True
322
328
 
323
- except docker.errors.ImageNotFound:
329
+ except ImageNotFound:
324
330
  print(f"Image {image_name}:{tag} not found.")
325
331
  with Spinner("Loading "):
326
332
  # We use docker cli here because it shows the download.
@@ -332,7 +338,7 @@ class DockerManager:
332
338
  local_image = self.client.images.get(f"{image_name}:{tag}")
333
339
  local_image_hash = local_image.id
334
340
  print(f"Image {image_name}:{tag} downloaded successfully.")
335
- except docker.errors.ImageNotFound:
341
+ except ImageNotFound:
336
342
  warnings.warn(f"Image {image_name}:{tag} not found after download.")
337
343
  return True
338
344
 
@@ -354,8 +360,11 @@ class DockerManager:
354
360
  """Compare if existing container has matching configuration"""
355
361
  try:
356
362
  # Check if container is using the same image
357
- container_image_id = container.image.id
358
- container_image_tags = container.image.tags
363
+ image = container.image
364
+ assert image is not None
365
+ container_image_id = image.id
366
+ container_image_tags = image.tags
367
+ assert container_image_id is not None
359
368
 
360
369
  # Simplified image comparison - just compare the IDs directly
361
370
  if not container_image_tags:
@@ -421,7 +430,7 @@ class DockerManager:
421
430
  return False
422
431
  except KeyboardInterrupt:
423
432
  raise
424
- except docker.errors.NotFound:
433
+ except NotFound:
425
434
  print("Container not found.")
426
435
  return False
427
436
  except Exception as e:
@@ -502,13 +511,13 @@ class DockerManager:
502
511
  print(out_msg)
503
512
  print("#" * msg_len + "\n")
504
513
  container = self.client.containers.run(
505
- image_name,
506
- command,
514
+ image=image_name,
515
+ command=command,
507
516
  name=container_name,
508
517
  detach=True,
509
518
  tty=True,
510
519
  volumes=volumes,
511
- ports=ports,
520
+ ports=ports, # type: ignore
512
521
  remove=True,
513
522
  )
514
523
  return container
@@ -563,7 +572,10 @@ class DockerManager:
563
572
  Returns a RunningContainer object that can be used to stop monitoring.
564
573
  """
565
574
  if isinstance(container, str):
566
- container = self.get_container(container)
575
+ container_name = container
576
+ tmp = self.get_container(container)
577
+ assert tmp is not None, f"Container {container_name} not found."
578
+ container = tmp
567
579
 
568
580
  assert container is not None, "Container not found."
569
581
 
@@ -580,10 +592,13 @@ class DockerManager:
580
592
  """
581
593
  if isinstance(container, str):
582
594
  container_name = container
583
- container = self.get_container(container)
584
- if not container:
595
+ # container = self.get_container(container)
596
+ tmp = self.get_container(container_name)
597
+ if not tmp:
585
598
  print(f"Could not put container {container_name} to sleep.")
586
599
  return
600
+ container = tmp
601
+ assert isinstance(container, Container)
587
602
  try:
588
603
  if platform.system() == "Windows":
589
604
  container.pause()
@@ -600,16 +615,27 @@ class DockerManager:
600
615
  """
601
616
  Resume (unpause) the container.
602
617
  """
618
+ container_name = "UNKNOWN"
603
619
  if isinstance(container, str):
604
- container = self.get_container(container)
620
+ container_name = container
621
+ container_or_none = self.get_container(container)
622
+ if container_or_none is None:
623
+ print(f"Could not resume container {container}.")
624
+ return
625
+ container = container_or_none
626
+ container_name = container.name
627
+ elif isinstance(container, Container):
628
+ container_name = container.name
629
+ assert isinstance(container, Container)
605
630
  if not container:
606
631
  print(f"Could not resume container {container}.")
607
632
  return
608
633
  try:
634
+ assert isinstance(container, Container)
609
635
  container.unpause()
610
636
  print(f"Container {container.name} has been resumed.")
611
637
  except Exception as e:
612
- print(f"Failed to resume container {container.name}: {e}")
638
+ print(f"Failed to resume container {container_name}: {e}")
613
639
 
614
640
  def get_container(self, container_name: str) -> Container | None:
615
641
  """
@@ -753,6 +779,7 @@ def main():
753
779
  # new_tag = "my-python"
754
780
  container_name = "my-python-container"
755
781
  command = "python -m http.server"
782
+ running_container: RunningContainer | None = None
756
783
 
757
784
  try:
758
785
  # Step 1: Validate or download the image
@@ -775,13 +802,18 @@ def main():
775
802
 
776
803
  except KeyboardInterrupt:
777
804
  print("\nStopping container...")
778
- running_container.stop()
779
- container = docker_manager.get_container(container_name)
780
- docker_manager.suspend_container(container)
805
+ if isinstance(running_container, RunningContainer):
806
+ running_container.stop()
807
+ container_or_none = docker_manager.get_container(container_name)
808
+ if container_or_none is not None:
809
+ docker_manager.suspend_container(container_or_none)
810
+ else:
811
+ warnings.warn(f"Container {container_name} not found.")
781
812
 
782
813
  try:
783
814
  # Suspend and resume the container
784
815
  container = docker_manager.get_container(container_name)
816
+ assert container is not None, "Container not found."
785
817
  docker_manager.suspend_container(container)
786
818
 
787
819
  input("Press Enter to resume the container...")
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()