fastled 1.4.31__py3-none-any.whl → 1.4.33__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/app.py +10 -0
- fastled/args.py +15 -0
- fastled/client_server.py +46 -15
- fastled/install/__init__.py +1 -0
- fastled/install/examples_manager.py +62 -0
- fastled/install/extension_manager.py +113 -0
- fastled/install/main.py +156 -0
- fastled/install/project_detection.py +167 -0
- fastled/install/test_install.py +373 -0
- fastled/install/vscode_config.py +167 -0
- fastled/open_browser.py +7 -4
- fastled/parse_args.py +45 -0
- fastled/playwright/playwright_browser.py +67 -160
- fastled/playwright/resize_tracking.py +127 -0
- fastled/print_filter.py +52 -52
- fastled/string_diff.py +165 -165
- fastled/types.py +4 -0
- fastled/version.py +41 -41
- fastled/web_compile.py +80 -5
- {fastled-1.4.31.dist-info → fastled-1.4.33.dist-info}/METADATA +531 -531
- {fastled-1.4.31.dist-info → fastled-1.4.33.dist-info}/RECORD +26 -18
- {fastled-1.4.31.dist-info → fastled-1.4.33.dist-info}/WHEEL +0 -0
- {fastled-1.4.31.dist-info → fastled-1.4.33.dist-info}/entry_points.txt +0 -0
- {fastled-1.4.31.dist-info → fastled-1.4.33.dist-info}/licenses/LICENSE +0 -0
- {fastled-1.4.31.dist-info → fastled-1.4.33.dist-info}/top_level.txt +0 -0
fastled/string_diff.py
CHANGED
@@ -1,165 +1,165 @@
|
|
1
|
-
from pathlib import Path
|
2
|
-
|
3
|
-
from rapidfuzz import fuzz
|
4
|
-
|
5
|
-
|
6
|
-
def _filter_out_obvious_bad_choices(
|
7
|
-
input_str: str, string_list: list[str]
|
8
|
-
) -> list[str]:
|
9
|
-
"""
|
10
|
-
Filter out strings that are too different from the input string.
|
11
|
-
This is a heuristic and may not be perfect.
|
12
|
-
"""
|
13
|
-
if not input_str.strip(): # Handle empty input
|
14
|
-
return string_list
|
15
|
-
|
16
|
-
input_chars = set(input_str.lower())
|
17
|
-
filtered_list = []
|
18
|
-
for s in string_list:
|
19
|
-
# Check if at least half of the input characters are in the string
|
20
|
-
s_chars = set(s.lower())
|
21
|
-
common_chars = input_chars.intersection(s_chars)
|
22
|
-
if len(common_chars) >= len(input_chars) / 2:
|
23
|
-
filtered_list.append(s)
|
24
|
-
return filtered_list
|
25
|
-
|
26
|
-
|
27
|
-
def is_in_order_match(input_str: str, other: str) -> bool:
|
28
|
-
"""
|
29
|
-
Check if the input string is an in-order match for any string in the list.
|
30
|
-
An in-order match means that the characters of the input string appear
|
31
|
-
in the same order in the string from the list, ignoring spaces in the input.
|
32
|
-
"""
|
33
|
-
|
34
|
-
# Remove spaces from input string for matching
|
35
|
-
input_chars = [c.lower() for c in input_str if c != " "]
|
36
|
-
other_chars = [c.lower() for c in other]
|
37
|
-
input_index = 0
|
38
|
-
other_index = 0
|
39
|
-
while input_index < len(input_chars) and other_index < len(other_chars):
|
40
|
-
if input_chars[input_index] == other_chars[other_index]:
|
41
|
-
input_index += 1
|
42
|
-
other_index += 1
|
43
|
-
# If we reached the end of the input string, it means all characters were found in order
|
44
|
-
if input_index == len(input_chars):
|
45
|
-
return True
|
46
|
-
return False
|
47
|
-
|
48
|
-
|
49
|
-
# Returns the min distance strings. If there is a tie, it returns
|
50
|
-
# all the strings that have the same min distance.
|
51
|
-
# Returns a tuple of index and string.
|
52
|
-
def string_diff(
|
53
|
-
input_string: str, string_list: list[str], ignore_case=True
|
54
|
-
) -> list[tuple[float, str]]:
|
55
|
-
|
56
|
-
def normalize(s: str) -> str:
|
57
|
-
return s.lower() if ignore_case else s
|
58
|
-
|
59
|
-
# Handle empty input or empty list
|
60
|
-
if not input_string.strip():
|
61
|
-
# Return all strings with equal distance for empty input
|
62
|
-
return [(i, s) for i, s in enumerate(string_list)]
|
63
|
-
|
64
|
-
if not string_list:
|
65
|
-
return []
|
66
|
-
|
67
|
-
map_string: dict[str, str] = {}
|
68
|
-
|
69
|
-
if ignore_case:
|
70
|
-
map_string = {s.lower(): s for s in string_list}
|
71
|
-
else:
|
72
|
-
map_string = {s: s for s in string_list}
|
73
|
-
|
74
|
-
original_string_list = string_list.copy()
|
75
|
-
if ignore_case:
|
76
|
-
string_list = [s.lower() for s in string_list]
|
77
|
-
input_string = input_string.lower()
|
78
|
-
|
79
|
-
# Check for exact matches, but also check if there are other substring matches
|
80
|
-
exact_matches = [s for s in string_list if s == input_string]
|
81
|
-
substring_matches = [s for s in string_list if input_string in s]
|
82
|
-
|
83
|
-
# If there's an exact match AND other substring matches, return all substring matches
|
84
|
-
# This provides better user experience for partial matching
|
85
|
-
if exact_matches and len(substring_matches) > 1:
|
86
|
-
out: list[tuple[float, str]] = []
|
87
|
-
for i, s in enumerate(substring_matches):
|
88
|
-
s_mapped = map_string.get(s, s)
|
89
|
-
out.append((i, s_mapped))
|
90
|
-
return out
|
91
|
-
|
92
|
-
# If there's only an exact match and no other substring matches, return just the exact match
|
93
|
-
if exact_matches and len(substring_matches) == 1:
|
94
|
-
out: list[tuple[float, str]] = []
|
95
|
-
for i, s in enumerate(exact_matches):
|
96
|
-
s_mapped = map_string.get(s, s)
|
97
|
-
out.append((i, s_mapped))
|
98
|
-
return out
|
99
|
-
|
100
|
-
# Apply set membership filtering for queries with 3+ characters
|
101
|
-
if len(input_string.strip()) >= 3:
|
102
|
-
filtered = _filter_out_obvious_bad_choices(input_string, string_list)
|
103
|
-
if filtered: # Only apply filter if it doesn't eliminate everything
|
104
|
-
string_list = filtered
|
105
|
-
|
106
|
-
# Second filter: exact substring filtering if applicable
|
107
|
-
if substring_matches:
|
108
|
-
string_list = substring_matches
|
109
|
-
# Return all substring matches
|
110
|
-
out: list[tuple[float, str]] = []
|
111
|
-
for i, s in enumerate(string_list):
|
112
|
-
s_mapped = map_string.get(s, s)
|
113
|
-
out.append((i, s_mapped))
|
114
|
-
return out
|
115
|
-
|
116
|
-
# Third filter: in order exact match filtering if applicable.
|
117
|
-
in_order_matches = [s for s in string_list if is_in_order_match(input_string, s)]
|
118
|
-
if in_order_matches:
|
119
|
-
string_list = in_order_matches
|
120
|
-
|
121
|
-
# Calculate distances
|
122
|
-
distances: list[float] = []
|
123
|
-
for s in string_list:
|
124
|
-
dist = fuzz.token_sort_ratio(normalize(input_string), normalize(s))
|
125
|
-
distances.append(1.0 / (dist + 1.0))
|
126
|
-
|
127
|
-
# Handle case where no strings remain after filtering
|
128
|
-
if not distances:
|
129
|
-
# Fall back to original list and calculate distances
|
130
|
-
string_list = original_string_list.copy()
|
131
|
-
if ignore_case:
|
132
|
-
string_list = [s.lower() for s in string_list]
|
133
|
-
|
134
|
-
distances = []
|
135
|
-
for s in string_list:
|
136
|
-
dist = fuzz.token_sort_ratio(normalize(input_string), normalize(s))
|
137
|
-
distances.append(1.0 / (dist + 1.0))
|
138
|
-
|
139
|
-
min_distance = min(distances)
|
140
|
-
out: list[tuple[float, str]] = []
|
141
|
-
for i, d in enumerate(distances):
|
142
|
-
if d == min_distance:
|
143
|
-
s = string_list[i]
|
144
|
-
s_mapped = map_string.get(s, s)
|
145
|
-
out.append((i, s_mapped))
|
146
|
-
|
147
|
-
return out
|
148
|
-
|
149
|
-
|
150
|
-
def string_diff_paths(
|
151
|
-
input_string: str | Path, path_list: list[Path], ignore_case=True
|
152
|
-
) -> list[tuple[float, Path]]:
|
153
|
-
# Normalize path separators to forward slashes for consistent comparison
|
154
|
-
string_list = [str(p).replace("\\", "/") for p in path_list]
|
155
|
-
input_str = str(input_string).replace("\\", "/")
|
156
|
-
|
157
|
-
tmp = string_diff(input_str, string_list, ignore_case)
|
158
|
-
out: list[tuple[float, Path]] = []
|
159
|
-
for i, j in tmp:
|
160
|
-
# Find the original path that matches the normalized result
|
161
|
-
for idx, orig_path in enumerate(path_list):
|
162
|
-
if str(orig_path).replace("\\", "/") == j:
|
163
|
-
out.append((i, orig_path))
|
164
|
-
break
|
165
|
-
return out
|
1
|
+
from pathlib import Path
|
2
|
+
|
3
|
+
from rapidfuzz import fuzz
|
4
|
+
|
5
|
+
|
6
|
+
def _filter_out_obvious_bad_choices(
|
7
|
+
input_str: str, string_list: list[str]
|
8
|
+
) -> list[str]:
|
9
|
+
"""
|
10
|
+
Filter out strings that are too different from the input string.
|
11
|
+
This is a heuristic and may not be perfect.
|
12
|
+
"""
|
13
|
+
if not input_str.strip(): # Handle empty input
|
14
|
+
return string_list
|
15
|
+
|
16
|
+
input_chars = set(input_str.lower())
|
17
|
+
filtered_list = []
|
18
|
+
for s in string_list:
|
19
|
+
# Check if at least half of the input characters are in the string
|
20
|
+
s_chars = set(s.lower())
|
21
|
+
common_chars = input_chars.intersection(s_chars)
|
22
|
+
if len(common_chars) >= len(input_chars) / 2:
|
23
|
+
filtered_list.append(s)
|
24
|
+
return filtered_list
|
25
|
+
|
26
|
+
|
27
|
+
def is_in_order_match(input_str: str, other: str) -> bool:
|
28
|
+
"""
|
29
|
+
Check if the input string is an in-order match for any string in the list.
|
30
|
+
An in-order match means that the characters of the input string appear
|
31
|
+
in the same order in the string from the list, ignoring spaces in the input.
|
32
|
+
"""
|
33
|
+
|
34
|
+
# Remove spaces from input string for matching
|
35
|
+
input_chars = [c.lower() for c in input_str if c != " "]
|
36
|
+
other_chars = [c.lower() for c in other]
|
37
|
+
input_index = 0
|
38
|
+
other_index = 0
|
39
|
+
while input_index < len(input_chars) and other_index < len(other_chars):
|
40
|
+
if input_chars[input_index] == other_chars[other_index]:
|
41
|
+
input_index += 1
|
42
|
+
other_index += 1
|
43
|
+
# If we reached the end of the input string, it means all characters were found in order
|
44
|
+
if input_index == len(input_chars):
|
45
|
+
return True
|
46
|
+
return False
|
47
|
+
|
48
|
+
|
49
|
+
# Returns the min distance strings. If there is a tie, it returns
|
50
|
+
# all the strings that have the same min distance.
|
51
|
+
# Returns a tuple of index and string.
|
52
|
+
def string_diff(
|
53
|
+
input_string: str, string_list: list[str], ignore_case=True
|
54
|
+
) -> list[tuple[float, str]]:
|
55
|
+
|
56
|
+
def normalize(s: str) -> str:
|
57
|
+
return s.lower() if ignore_case else s
|
58
|
+
|
59
|
+
# Handle empty input or empty list
|
60
|
+
if not input_string.strip():
|
61
|
+
# Return all strings with equal distance for empty input
|
62
|
+
return [(i, s) for i, s in enumerate(string_list)]
|
63
|
+
|
64
|
+
if not string_list:
|
65
|
+
return []
|
66
|
+
|
67
|
+
map_string: dict[str, str] = {}
|
68
|
+
|
69
|
+
if ignore_case:
|
70
|
+
map_string = {s.lower(): s for s in string_list}
|
71
|
+
else:
|
72
|
+
map_string = {s: s for s in string_list}
|
73
|
+
|
74
|
+
original_string_list = string_list.copy()
|
75
|
+
if ignore_case:
|
76
|
+
string_list = [s.lower() for s in string_list]
|
77
|
+
input_string = input_string.lower()
|
78
|
+
|
79
|
+
# Check for exact matches, but also check if there are other substring matches
|
80
|
+
exact_matches = [s for s in string_list if s == input_string]
|
81
|
+
substring_matches = [s for s in string_list if input_string in s]
|
82
|
+
|
83
|
+
# If there's an exact match AND other substring matches, return all substring matches
|
84
|
+
# This provides better user experience for partial matching
|
85
|
+
if exact_matches and len(substring_matches) > 1:
|
86
|
+
out: list[tuple[float, str]] = []
|
87
|
+
for i, s in enumerate(substring_matches):
|
88
|
+
s_mapped = map_string.get(s, s)
|
89
|
+
out.append((i, s_mapped))
|
90
|
+
return out
|
91
|
+
|
92
|
+
# If there's only an exact match and no other substring matches, return just the exact match
|
93
|
+
if exact_matches and len(substring_matches) == 1:
|
94
|
+
out: list[tuple[float, str]] = []
|
95
|
+
for i, s in enumerate(exact_matches):
|
96
|
+
s_mapped = map_string.get(s, s)
|
97
|
+
out.append((i, s_mapped))
|
98
|
+
return out
|
99
|
+
|
100
|
+
# Apply set membership filtering for queries with 3+ characters
|
101
|
+
if len(input_string.strip()) >= 3:
|
102
|
+
filtered = _filter_out_obvious_bad_choices(input_string, string_list)
|
103
|
+
if filtered: # Only apply filter if it doesn't eliminate everything
|
104
|
+
string_list = filtered
|
105
|
+
|
106
|
+
# Second filter: exact substring filtering if applicable
|
107
|
+
if substring_matches:
|
108
|
+
string_list = substring_matches
|
109
|
+
# Return all substring matches
|
110
|
+
out: list[tuple[float, str]] = []
|
111
|
+
for i, s in enumerate(string_list):
|
112
|
+
s_mapped = map_string.get(s, s)
|
113
|
+
out.append((i, s_mapped))
|
114
|
+
return out
|
115
|
+
|
116
|
+
# Third filter: in order exact match filtering if applicable.
|
117
|
+
in_order_matches = [s for s in string_list if is_in_order_match(input_string, s)]
|
118
|
+
if in_order_matches:
|
119
|
+
string_list = in_order_matches
|
120
|
+
|
121
|
+
# Calculate distances
|
122
|
+
distances: list[float] = []
|
123
|
+
for s in string_list:
|
124
|
+
dist = fuzz.token_sort_ratio(normalize(input_string), normalize(s))
|
125
|
+
distances.append(1.0 / (dist + 1.0))
|
126
|
+
|
127
|
+
# Handle case where no strings remain after filtering
|
128
|
+
if not distances:
|
129
|
+
# Fall back to original list and calculate distances
|
130
|
+
string_list = original_string_list.copy()
|
131
|
+
if ignore_case:
|
132
|
+
string_list = [s.lower() for s in string_list]
|
133
|
+
|
134
|
+
distances = []
|
135
|
+
for s in string_list:
|
136
|
+
dist = fuzz.token_sort_ratio(normalize(input_string), normalize(s))
|
137
|
+
distances.append(1.0 / (dist + 1.0))
|
138
|
+
|
139
|
+
min_distance = min(distances)
|
140
|
+
out: list[tuple[float, str]] = []
|
141
|
+
for i, d in enumerate(distances):
|
142
|
+
if d == min_distance:
|
143
|
+
s = string_list[i]
|
144
|
+
s_mapped = map_string.get(s, s)
|
145
|
+
out.append((i, s_mapped))
|
146
|
+
|
147
|
+
return out
|
148
|
+
|
149
|
+
|
150
|
+
def string_diff_paths(
|
151
|
+
input_string: str | Path, path_list: list[Path], ignore_case=True
|
152
|
+
) -> list[tuple[float, Path]]:
|
153
|
+
# Normalize path separators to forward slashes for consistent comparison
|
154
|
+
string_list = [str(p).replace("\\", "/") for p in path_list]
|
155
|
+
input_str = str(input_string).replace("\\", "/")
|
156
|
+
|
157
|
+
tmp = string_diff(input_str, string_list, ignore_case)
|
158
|
+
out: list[tuple[float, Path]] = []
|
159
|
+
for i, j in tmp:
|
160
|
+
# Find the original path that matches the normalized result
|
161
|
+
for idx, orig_path in enumerate(path_list):
|
162
|
+
if str(orig_path).replace("\\", "/") == j:
|
163
|
+
out.append((i, orig_path))
|
164
|
+
break
|
165
|
+
return out
|
fastled/types.py
CHANGED
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()
|
fastled/web_compile.py
CHANGED
@@ -196,16 +196,30 @@ def _process_compile_response(
|
|
196
196
|
response: httpx.Response,
|
197
197
|
zip_result: ZipResult,
|
198
198
|
start_time: float,
|
199
|
+
zip_time: float,
|
200
|
+
libfastled_time: float,
|
201
|
+
sketch_time: float,
|
199
202
|
) -> CompileResult:
|
200
203
|
"""Process the compile response and return the final result."""
|
201
204
|
if response.status_code != 200:
|
202
205
|
json_response = response.json()
|
203
206
|
detail = json_response.get("detail", "Could not compile")
|
204
207
|
return CompileResult(
|
205
|
-
success=False,
|
208
|
+
success=False,
|
209
|
+
stdout=detail,
|
210
|
+
hash_value=None,
|
211
|
+
zip_bytes=b"",
|
212
|
+
zip_time=zip_time,
|
213
|
+
libfastled_time=libfastled_time,
|
214
|
+
sketch_time=sketch_time,
|
215
|
+
response_processing_time=0.0, # No response processing in error case
|
206
216
|
)
|
207
217
|
|
208
218
|
print(f"Response status code: {response}")
|
219
|
+
|
220
|
+
# Time the response processing
|
221
|
+
response_processing_start = time.time()
|
222
|
+
|
209
223
|
# Create a temporary directory to extract the zip
|
210
224
|
with tempfile.TemporaryDirectory() as extract_dir:
|
211
225
|
extract_path = Path(extract_dir)
|
@@ -250,14 +264,32 @@ def _process_compile_response(
|
|
250
264
|
relative_path = file_path.relative_to(extract_path)
|
251
265
|
out_zip.write(file_path, relative_path)
|
252
266
|
|
267
|
+
response_processing_time = time.time() - response_processing_start
|
253
268
|
diff_time = time.time() - start_time
|
254
|
-
|
269
|
+
|
270
|
+
# Create detailed timing breakdown
|
271
|
+
unaccounted_time = diff_time - (
|
272
|
+
zip_time + libfastled_time + sketch_time + response_processing_time
|
273
|
+
)
|
274
|
+
msg = f"Compilation success, took {diff_time:.2f} seconds\n"
|
275
|
+
msg += f" zip creation: {zip_time:.2f}\n"
|
276
|
+
if libfastled_time > 0:
|
277
|
+
msg += f" libfastled: {libfastled_time:.2f}\n"
|
278
|
+
msg += f" sketch compile + link: {sketch_time:.2f}\n"
|
279
|
+
msg += f" response processing: {response_processing_time:.2f}\n"
|
280
|
+
if unaccounted_time > 0.01: # Only show if significant
|
281
|
+
msg += f" other overhead: {unaccounted_time:.2f}"
|
282
|
+
|
255
283
|
_print_banner(msg)
|
256
284
|
return CompileResult(
|
257
285
|
success=True,
|
258
286
|
stdout=stdout,
|
259
287
|
hash_value=hash_value,
|
260
288
|
zip_bytes=out_buffer.getvalue(),
|
289
|
+
zip_time=zip_time,
|
290
|
+
libfastled_time=libfastled_time,
|
291
|
+
sketch_time=sketch_time,
|
292
|
+
response_processing_time=response_processing_time,
|
261
293
|
)
|
262
294
|
|
263
295
|
|
@@ -279,17 +311,35 @@ def web_compile(
|
|
279
311
|
auth_token = auth_token or AUTH_TOKEN
|
280
312
|
if not directory.exists():
|
281
313
|
raise FileNotFoundError(f"Directory not found: {directory}")
|
314
|
+
|
315
|
+
# Time the zip creation
|
316
|
+
zip_start_time = time.time()
|
282
317
|
zip_result: ZipResult | Exception = zip_files(directory, build_mode=build_mode)
|
318
|
+
zip_time = time.time() - zip_start_time
|
319
|
+
|
283
320
|
if isinstance(zip_result, Exception):
|
284
321
|
return CompileResult(
|
285
|
-
success=False,
|
322
|
+
success=False,
|
323
|
+
stdout=str(zip_result),
|
324
|
+
hash_value=None,
|
325
|
+
zip_bytes=b"",
|
326
|
+
zip_time=zip_time,
|
327
|
+
libfastled_time=0.0, # No libfastled compilation in zip error case
|
328
|
+
sketch_time=0.0, # No sketch compilation in zip error case
|
329
|
+
response_processing_time=0.0, # No response processing in zip error case
|
286
330
|
)
|
287
331
|
zip_bytes = zip_result.zip_bytes
|
288
332
|
print(f"Web compiling on {host}...")
|
333
|
+
|
334
|
+
# Track timing for each step
|
335
|
+
libfastled_time = 0.0
|
336
|
+
sketch_time = 0.0
|
337
|
+
|
289
338
|
try:
|
290
339
|
# Step 1: Compile libfastled if requested
|
291
340
|
if allow_libcompile:
|
292
341
|
print("Step 1: Compiling libfastled...")
|
342
|
+
libfastled_start_time = time.time()
|
293
343
|
try:
|
294
344
|
libfastled_response = _compile_libfastled(host, auth_token, build_mode)
|
295
345
|
|
@@ -308,6 +358,10 @@ def web_compile(
|
|
308
358
|
stdout=stdout,
|
309
359
|
hash_value=None,
|
310
360
|
zip_bytes=b"",
|
361
|
+
zip_time=zip_time,
|
362
|
+
libfastled_time=libfastled_time,
|
363
|
+
sketch_time=0.0, # No sketch compilation when libfastled fails
|
364
|
+
response_processing_time=0.0, # No response processing when libfastled fails
|
311
365
|
)
|
312
366
|
else:
|
313
367
|
# Check for embedded HTTP status in response content
|
@@ -332,6 +386,10 @@ def web_compile(
|
|
332
386
|
stdout=stdout,
|
333
387
|
hash_value=None,
|
334
388
|
zip_bytes=b"",
|
389
|
+
zip_time=zip_time,
|
390
|
+
libfastled_time=libfastled_time,
|
391
|
+
sketch_time=0.0, # No sketch compilation when libfastled fails
|
392
|
+
response_processing_time=0.0, # No response processing when libfastled fails
|
335
393
|
)
|
336
394
|
# Continue with sketch compilation even if libfastled fails
|
337
395
|
elif embedded_status is None:
|
@@ -343,7 +401,9 @@ def web_compile(
|
|
343
401
|
print("✅ libfastled compilation successful")
|
344
402
|
else:
|
345
403
|
print("✅ libfastled compilation successful")
|
404
|
+
libfastled_time = time.time() - libfastled_start_time
|
346
405
|
except Exception as e:
|
406
|
+
libfastled_time = time.time() - libfastled_start_time
|
347
407
|
print(f"Warning: libfastled compilation failed: {e}")
|
348
408
|
# Continue with sketch compilation even if libfastled fails
|
349
409
|
else:
|
@@ -351,6 +411,7 @@ def web_compile(
|
|
351
411
|
|
352
412
|
# Step 2: Compile the sketch
|
353
413
|
print("Step 2: Compiling sketch...")
|
414
|
+
sketch_start_time = time.time()
|
354
415
|
response = _send_compile_request(
|
355
416
|
host,
|
356
417
|
zip_bytes,
|
@@ -360,8 +421,11 @@ def web_compile(
|
|
360
421
|
no_platformio,
|
361
422
|
False, # allow_libcompile is always False since we handle it manually
|
362
423
|
)
|
424
|
+
sketch_time = time.time() - sketch_start_time
|
363
425
|
|
364
|
-
return _process_compile_response(
|
426
|
+
return _process_compile_response(
|
427
|
+
response, zip_result, start_time, zip_time, libfastled_time, sketch_time
|
428
|
+
)
|
365
429
|
|
366
430
|
except ConnectionError as e:
|
367
431
|
_print_banner(str(e))
|
@@ -370,6 +434,10 @@ def web_compile(
|
|
370
434
|
stdout=str(e),
|
371
435
|
hash_value=None,
|
372
436
|
zip_bytes=b"",
|
437
|
+
zip_time=zip_time,
|
438
|
+
libfastled_time=libfastled_time,
|
439
|
+
sketch_time=sketch_time,
|
440
|
+
response_processing_time=0.0, # No response processing in connection error case
|
373
441
|
)
|
374
442
|
except KeyboardInterrupt:
|
375
443
|
print("Keyboard interrupt")
|
@@ -377,5 +445,12 @@ def web_compile(
|
|
377
445
|
except httpx.HTTPError as e:
|
378
446
|
print(f"Error: {e}")
|
379
447
|
return CompileResult(
|
380
|
-
success=False,
|
448
|
+
success=False,
|
449
|
+
stdout=str(e),
|
450
|
+
hash_value=None,
|
451
|
+
zip_bytes=b"",
|
452
|
+
zip_time=zip_time,
|
453
|
+
libfastled_time=libfastled_time,
|
454
|
+
sketch_time=sketch_time,
|
455
|
+
response_processing_time=0.0, # No response processing in HTTP error case
|
381
456
|
)
|