fastled 1.4.42__py3-none-any.whl → 1.4.44__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 +1 -1
- fastled/print_filter.py +52 -52
- fastled/project_init.py +20 -13
- fastled/select_sketch_directory.py +101 -47
- fastled/version.py +41 -41
- {fastled-1.4.42.dist-info → fastled-1.4.44.dist-info}/METADATA +532 -532
- {fastled-1.4.42.dist-info → fastled-1.4.44.dist-info}/RECORD +11 -11
- {fastled-1.4.42.dist-info → fastled-1.4.44.dist-info}/WHEEL +0 -0
- {fastled-1.4.42.dist-info → fastled-1.4.44.dist-info}/entry_points.txt +0 -0
- {fastled-1.4.42.dist-info → fastled-1.4.44.dist-info}/licenses/LICENSE +0 -0
- {fastled-1.4.42.dist-info → fastled-1.4.44.dist-info}/top_level.txt +0 -0
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.
|
4
|
+
__version__ = "1.4.44"
|
5
5
|
|
6
6
|
__version_url_latest__ = "https://raw.githubusercontent.com/zackees/fastled-wasm/refs/heads/main/src/fastled/__version__.py"
|
fastled/print_filter.py
CHANGED
@@ -1,52 +1,52 @@
|
|
1
|
-
import re
|
2
|
-
from abc import ABC, abstractmethod
|
3
|
-
from enum import Enum
|
4
|
-
|
5
|
-
|
6
|
-
class PrintFilter(ABC):
|
7
|
-
"""Abstract base class for filtering text output."""
|
8
|
-
|
9
|
-
def __init__(self, echo: bool = True) -> None:
|
10
|
-
self.echo = echo
|
11
|
-
|
12
|
-
@abstractmethod
|
13
|
-
def filter(self, text: str) -> str:
|
14
|
-
"""Filter the text according to implementation-specific rules."""
|
15
|
-
pass
|
16
|
-
|
17
|
-
def print(self, text: str | bytes) -> str:
|
18
|
-
"""Prints the text to the console after filtering."""
|
19
|
-
if isinstance(text, bytes):
|
20
|
-
text = text.decode("utf-8")
|
21
|
-
text = self.filter(text)
|
22
|
-
if self.echo:
|
23
|
-
print(text, end="")
|
24
|
-
return text
|
25
|
-
|
26
|
-
|
27
|
-
def _handle_ino_cpp(line: str) -> str:
|
28
|
-
if ".ino.cpp" in line[0:30]:
|
29
|
-
# Extract the filename without path and extension
|
30
|
-
match = re.search(r"src/([^/]+)\.ino\.cpp", line)
|
31
|
-
if match:
|
32
|
-
filename = match.group(1)
|
33
|
-
# Replace with examples/Filename/Filename.ino format
|
34
|
-
line = line.replace(
|
35
|
-
f"src/{filename}.ino.cpp", f"examples/{filename}/{filename}.ino"
|
36
|
-
)
|
37
|
-
else:
|
38
|
-
# Fall back to simple extension replacement if regex doesn't match
|
39
|
-
line = line.replace(".ino.cpp", ".ino")
|
40
|
-
return line
|
41
|
-
|
42
|
-
|
43
|
-
class PrintFilterDefault(PrintFilter):
|
44
|
-
"""Provides default filtering for FastLED output."""
|
45
|
-
|
46
|
-
def filter(self, text: str) -> str:
|
47
|
-
return text
|
48
|
-
|
49
|
-
|
50
|
-
class CompileOrLink(Enum):
|
51
|
-
COMPILE = "compile"
|
52
|
-
LINK = "link"
|
1
|
+
import re
|
2
|
+
from abc import ABC, abstractmethod
|
3
|
+
from enum import Enum
|
4
|
+
|
5
|
+
|
6
|
+
class PrintFilter(ABC):
|
7
|
+
"""Abstract base class for filtering text output."""
|
8
|
+
|
9
|
+
def __init__(self, echo: bool = True) -> None:
|
10
|
+
self.echo = echo
|
11
|
+
|
12
|
+
@abstractmethod
|
13
|
+
def filter(self, text: str) -> str:
|
14
|
+
"""Filter the text according to implementation-specific rules."""
|
15
|
+
pass
|
16
|
+
|
17
|
+
def print(self, text: str | bytes) -> str:
|
18
|
+
"""Prints the text to the console after filtering."""
|
19
|
+
if isinstance(text, bytes):
|
20
|
+
text = text.decode("utf-8")
|
21
|
+
text = self.filter(text)
|
22
|
+
if self.echo:
|
23
|
+
print(text, end="")
|
24
|
+
return text
|
25
|
+
|
26
|
+
|
27
|
+
def _handle_ino_cpp(line: str) -> str:
|
28
|
+
if ".ino.cpp" in line[0:30]:
|
29
|
+
# Extract the filename without path and extension
|
30
|
+
match = re.search(r"src/([^/]+)\.ino\.cpp", line)
|
31
|
+
if match:
|
32
|
+
filename = match.group(1)
|
33
|
+
# Replace with examples/Filename/Filename.ino format
|
34
|
+
line = line.replace(
|
35
|
+
f"src/{filename}.ino.cpp", f"examples/{filename}/{filename}.ino"
|
36
|
+
)
|
37
|
+
else:
|
38
|
+
# Fall back to simple extension replacement if regex doesn't match
|
39
|
+
line = line.replace(".ino.cpp", ".ino")
|
40
|
+
return line
|
41
|
+
|
42
|
+
|
43
|
+
class PrintFilterDefault(PrintFilter):
|
44
|
+
"""Provides default filtering for FastLED output."""
|
45
|
+
|
46
|
+
def filter(self, text: str) -> str:
|
47
|
+
return text
|
48
|
+
|
49
|
+
|
50
|
+
class CompileOrLink(Enum):
|
51
|
+
COMPILE = "compile"
|
52
|
+
LINK = "link"
|
fastled/project_init.py
CHANGED
@@ -22,20 +22,27 @@ def get_examples(host: str | None = None) -> list[str]:
|
|
22
22
|
|
23
23
|
|
24
24
|
def _prompt_for_example() -> str:
|
25
|
+
from fastled.select_sketch_directory import _disambiguate_user_choice
|
26
|
+
|
25
27
|
examples = get_examples()
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
28
|
+
|
29
|
+
# Find default example index (prefer DEFAULT_EXAMPLE if it exists)
|
30
|
+
default_index = 0
|
31
|
+
if DEFAULT_EXAMPLE in examples:
|
32
|
+
default_index = examples.index(DEFAULT_EXAMPLE)
|
33
|
+
|
34
|
+
result = _disambiguate_user_choice(
|
35
|
+
examples,
|
36
|
+
option_to_str=lambda x: x,
|
37
|
+
prompt="Available examples:",
|
38
|
+
default_index=default_index,
|
39
|
+
)
|
40
|
+
|
41
|
+
if result is None:
|
42
|
+
# Fallback to DEFAULT_EXAMPLE if user cancelled
|
43
|
+
return DEFAULT_EXAMPLE
|
44
|
+
|
45
|
+
return result
|
39
46
|
|
40
47
|
|
41
48
|
class DownloadThread(threading.Thread):
|
@@ -1,8 +1,90 @@
|
|
1
1
|
from pathlib import Path
|
2
|
+
from typing import Callable, TypeVar, Union
|
2
3
|
|
3
|
-
|
4
|
+
T = TypeVar("T")
|
4
5
|
|
5
|
-
|
6
|
+
|
7
|
+
def _disambiguate_user_choice(
|
8
|
+
options: list[T],
|
9
|
+
option_to_str: Callable[[T], str] = str,
|
10
|
+
prompt: str = "Multiple matches found. Please choose:",
|
11
|
+
default_index: int = 0,
|
12
|
+
) -> Union[T, None]:
|
13
|
+
"""
|
14
|
+
Present multiple options to the user with a default selection.
|
15
|
+
|
16
|
+
Args:
|
17
|
+
options: List of options to choose from
|
18
|
+
option_to_str: Function to convert option to display string
|
19
|
+
prompt: Prompt message to show user
|
20
|
+
default_index: Index of the default option (0-based)
|
21
|
+
|
22
|
+
Returns:
|
23
|
+
Selected option or None if cancelled
|
24
|
+
"""
|
25
|
+
if not options:
|
26
|
+
return None
|
27
|
+
|
28
|
+
if len(options) == 1:
|
29
|
+
return options[0]
|
30
|
+
|
31
|
+
# Ensure default_index is valid
|
32
|
+
if default_index < 0 or default_index >= len(options):
|
33
|
+
default_index = 0
|
34
|
+
|
35
|
+
print(f"\n{prompt}")
|
36
|
+
for i, option in enumerate(options):
|
37
|
+
option_str = option_to_str(option)
|
38
|
+
if i == default_index:
|
39
|
+
print(f" [{i+1}]: [{option_str}]") # Default option shown in brackets
|
40
|
+
else:
|
41
|
+
print(f" [{i+1}]: {option_str}")
|
42
|
+
|
43
|
+
default_option_str = option_to_str(options[default_index])
|
44
|
+
user_input = input(
|
45
|
+
f"\nEnter number or name (default: [{default_option_str}]): "
|
46
|
+
).strip()
|
47
|
+
|
48
|
+
# Handle empty input - select default
|
49
|
+
if not user_input:
|
50
|
+
return options[default_index]
|
51
|
+
|
52
|
+
# Try to parse as number
|
53
|
+
try:
|
54
|
+
index = int(user_input) - 1
|
55
|
+
if 0 <= index < len(options):
|
56
|
+
return options[index]
|
57
|
+
except ValueError:
|
58
|
+
pass
|
59
|
+
|
60
|
+
# Try to match by name (case insensitive)
|
61
|
+
user_input_lower = user_input.lower()
|
62
|
+
for option in options:
|
63
|
+
option_str = option_to_str(option).lower()
|
64
|
+
if option_str == user_input_lower:
|
65
|
+
return option
|
66
|
+
|
67
|
+
# Try partial match
|
68
|
+
matches = []
|
69
|
+
for option in options:
|
70
|
+
option_str = option_to_str(option)
|
71
|
+
if user_input_lower in option_str.lower():
|
72
|
+
matches.append(option)
|
73
|
+
|
74
|
+
if len(matches) == 1:
|
75
|
+
return matches[0]
|
76
|
+
elif len(matches) > 1:
|
77
|
+
# Recursive disambiguation with the filtered matches
|
78
|
+
return _disambiguate_user_choice(
|
79
|
+
matches,
|
80
|
+
option_to_str,
|
81
|
+
f"Multiple partial matches for '{user_input}':",
|
82
|
+
0, # Reset default to first match
|
83
|
+
)
|
84
|
+
|
85
|
+
# No match found
|
86
|
+
print(f"No match found for '{user_input}'. Please try again.")
|
87
|
+
return _disambiguate_user_choice(options, option_to_str, prompt, default_index)
|
6
88
|
|
7
89
|
|
8
90
|
def select_sketch_directory(
|
@@ -19,49 +101,21 @@ def select_sketch_directory(
|
|
19
101
|
print(f"\nUsing sketch directory: {sketch_directories[0]}")
|
20
102
|
return str(sketch_directories[0])
|
21
103
|
elif len(sketch_directories) > 1:
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
dist = fuzz.token_sort_ratio(which.lower(), path_str.lower())
|
40
|
-
distances.append((dist, path))
|
41
|
-
|
42
|
-
# Get the best distance and return the closest match(es)
|
43
|
-
best_distance = max(distances, key=lambda x: x[0])[0]
|
44
|
-
best_matches = [
|
45
|
-
path for dist, path in distances if dist == best_distance
|
46
|
-
]
|
47
|
-
|
48
|
-
if len(best_matches) == 1:
|
49
|
-
example = best_matches[0]
|
50
|
-
return str(example)
|
51
|
-
else:
|
52
|
-
# If still multiple matches with same distance, recurse again
|
53
|
-
return select_sketch_directory(
|
54
|
-
best_matches, cwd_is_fastled, is_followup=True
|
55
|
-
)
|
56
|
-
else:
|
57
|
-
# First call - use original fuzzy matching (allows ambiguity)
|
58
|
-
top_hits: list[tuple[float, Path]] = string_diff_paths(which, inputs)
|
59
|
-
if len(top_hits) == 1:
|
60
|
-
example = top_hits[0][1]
|
61
|
-
return str(example)
|
62
|
-
else:
|
63
|
-
# Recursive call with is_followup=True for more precise matching
|
64
|
-
return select_sketch_directory(
|
65
|
-
[p for _, p in top_hits], cwd_is_fastled, is_followup=True
|
66
|
-
)
|
104
|
+
if is_followup:
|
105
|
+
# Only disambiguate on follow-up calls
|
106
|
+
result = _disambiguate_user_choice(
|
107
|
+
sketch_directories,
|
108
|
+
option_to_str=lambda x: str(x),
|
109
|
+
prompt="Multiple Directories found, choose one:",
|
110
|
+
default_index=0,
|
111
|
+
)
|
112
|
+
|
113
|
+
if result is None:
|
114
|
+
return None
|
115
|
+
|
116
|
+
return str(result)
|
117
|
+
else:
|
118
|
+
# On first call, use the first directory automatically
|
119
|
+
print(f"\nUsing sketch directory: {sketch_directories[0]}")
|
120
|
+
return str(sketch_directories[0])
|
67
121
|
return None
|
fastled/version.py
CHANGED
@@ -1,41 +1,41 @@
|
|
1
|
-
from concurrent.futures import Future, ThreadPoolExecutor
|
2
|
-
|
3
|
-
import httpx
|
4
|
-
|
5
|
-
from fastled.__version__ import __version_url_latest__
|
6
|
-
|
7
|
-
|
8
|
-
def _fetch_version() -> str | Exception:
|
9
|
-
"""
|
10
|
-
Helper function to fetch the latest version from the GitHub repository.
|
11
|
-
"""
|
12
|
-
try:
|
13
|
-
response = httpx.get(__version_url_latest__)
|
14
|
-
response.raise_for_status()
|
15
|
-
# Extract the version string from the response text
|
16
|
-
version_line = response.text.split("__version__ = ")[1].split('"')[1]
|
17
|
-
return version_line
|
18
|
-
except Exception as e:
|
19
|
-
return e
|
20
|
-
|
21
|
-
|
22
|
-
def get_latest_version() -> Future[str | Exception]:
|
23
|
-
"""
|
24
|
-
Fetch the latest version from the GitHub repository.
|
25
|
-
Returns a future that will resolve with the version string or an exception.
|
26
|
-
"""
|
27
|
-
executor = ThreadPoolExecutor()
|
28
|
-
return executor.submit(_fetch_version)
|
29
|
-
|
30
|
-
|
31
|
-
def unit_test() -> None:
|
32
|
-
future = get_latest_version()
|
33
|
-
latest_version = future.result() # Wait for the future to complete
|
34
|
-
if isinstance(latest_version, Exception):
|
35
|
-
print(f"Error fetching latest version: {latest_version}")
|
36
|
-
else:
|
37
|
-
print(f"Latest version: {latest_version}")
|
38
|
-
|
39
|
-
|
40
|
-
if __name__ == "__main__":
|
41
|
-
unit_test()
|
1
|
+
from concurrent.futures import Future, ThreadPoolExecutor
|
2
|
+
|
3
|
+
import httpx
|
4
|
+
|
5
|
+
from fastled.__version__ import __version_url_latest__
|
6
|
+
|
7
|
+
|
8
|
+
def _fetch_version() -> str | Exception:
|
9
|
+
"""
|
10
|
+
Helper function to fetch the latest version from the GitHub repository.
|
11
|
+
"""
|
12
|
+
try:
|
13
|
+
response = httpx.get(__version_url_latest__)
|
14
|
+
response.raise_for_status()
|
15
|
+
# Extract the version string from the response text
|
16
|
+
version_line = response.text.split("__version__ = ")[1].split('"')[1]
|
17
|
+
return version_line
|
18
|
+
except Exception as e:
|
19
|
+
return e
|
20
|
+
|
21
|
+
|
22
|
+
def get_latest_version() -> Future[str | Exception]:
|
23
|
+
"""
|
24
|
+
Fetch the latest version from the GitHub repository.
|
25
|
+
Returns a future that will resolve with the version string or an exception.
|
26
|
+
"""
|
27
|
+
executor = ThreadPoolExecutor()
|
28
|
+
return executor.submit(_fetch_version)
|
29
|
+
|
30
|
+
|
31
|
+
def unit_test() -> None:
|
32
|
+
future = get_latest_version()
|
33
|
+
latest_version = future.result() # Wait for the future to complete
|
34
|
+
if isinstance(latest_version, Exception):
|
35
|
+
print(f"Error fetching latest version: {latest_version}")
|
36
|
+
else:
|
37
|
+
print(f"Latest version: {latest_version}")
|
38
|
+
|
39
|
+
|
40
|
+
if __name__ == "__main__":
|
41
|
+
unit_test()
|