circfirm 3.0.0__py3-none-any.whl → 4.0.0__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.
- circfirm/backend/__init__.py +18 -0
- circfirm/backend/cache.py +11 -27
- circfirm/backend/device.py +2 -2
- circfirm/backend/github.py +4 -4
- circfirm/backend/s3.py +10 -8
- circfirm/cli/__init__.py +23 -8
- circfirm/cli/cache.py +65 -9
- circfirm/cli/current.py +1 -3
- circfirm/cli/detect.py +38 -0
- circfirm/cli/install.py +13 -2
- circfirm/cli/query.py +7 -3
- circfirm/cli/update.py +64 -5
- circfirm/startup.py +3 -4
- {circfirm-3.0.0.dist-info → circfirm-4.0.0.dist-info}/METADATA +21 -19
- circfirm-4.0.0.dist-info/NOTICE +64 -0
- circfirm-4.0.0.dist-info/NOTICE.license +2 -0
- circfirm-4.0.0.dist-info/RECORD +27 -0
- {circfirm-3.0.0.dist-info → circfirm-4.0.0.dist-info}/WHEEL +1 -1
- circfirm-3.0.0.dist-info/RECORD +0 -24
- {circfirm-3.0.0.dist-info → circfirm-4.0.0.dist-info}/LICENSE +0 -0
- {circfirm-3.0.0.dist-info → circfirm-4.0.0.dist-info}/entry_points.txt +0 -0
- {circfirm-3.0.0.dist-info → circfirm-4.0.0.dist-info}/top_level.txt +0 -0
circfirm/backend/__init__.py
CHANGED
|
@@ -8,6 +8,7 @@ Author(s): Alec Delaney
|
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
10
|
import enum
|
|
11
|
+
import re
|
|
11
12
|
|
|
12
13
|
|
|
13
14
|
class Language(enum.Enum):
|
|
@@ -51,3 +52,20 @@ FIRMWARE_REGEX = (
|
|
|
51
52
|
.replace(r"[language]", f"({_ALL_LANGUAGES_REGEX})")
|
|
52
53
|
.replace(r"[version]", _VALID_VERSIONS_CAPTURE)
|
|
53
54
|
)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def get_uf2_filename(board_id: str, version: str, language: str = "en_US") -> str:
|
|
58
|
+
"""Get the structured name for a specific board/version CircuitPython."""
|
|
59
|
+
return f"adafruit-circuitpython-{board_id}-{language}-{version}.uf2"
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def parse_firmware_info(uf2_filename: str) -> tuple[str, str]:
|
|
63
|
+
"""Get firmware info."""
|
|
64
|
+
regex_match = re.match(FIRMWARE_REGEX, uf2_filename)
|
|
65
|
+
if regex_match is None:
|
|
66
|
+
raise ValueError(
|
|
67
|
+
"Firmware information could not be determined from the filename"
|
|
68
|
+
)
|
|
69
|
+
version = regex_match[3]
|
|
70
|
+
language = regex_match[2]
|
|
71
|
+
return version, language
|
circfirm/backend/cache.py
CHANGED
|
@@ -9,8 +9,7 @@ Author(s): Alec Delaney
|
|
|
9
9
|
|
|
10
10
|
import os
|
|
11
11
|
import pathlib
|
|
12
|
-
import
|
|
13
|
-
from typing import Dict, List, Optional, Set, Tuple
|
|
12
|
+
from typing import Optional
|
|
14
13
|
|
|
15
14
|
import packaging.version
|
|
16
15
|
import requests
|
|
@@ -19,16 +18,11 @@ import circfirm.backend
|
|
|
19
18
|
import circfirm.startup
|
|
20
19
|
|
|
21
20
|
|
|
22
|
-
def get_uf2_filename(board_id: str, version: str, language: str = "en_US") -> str:
|
|
23
|
-
"""Get the structured name for a specific board/version CircuitPython."""
|
|
24
|
-
return f"adafruit-circuitpython-{board_id}-{language}-{version}.uf2"
|
|
25
|
-
|
|
26
|
-
|
|
27
21
|
def get_uf2_filepath(
|
|
28
22
|
board_id: str, version: str, language: str = "en_US"
|
|
29
23
|
) -> pathlib.Path:
|
|
30
24
|
"""Get the path to a downloaded UF2 file."""
|
|
31
|
-
file = get_uf2_filename(board_id, version, language)
|
|
25
|
+
file = circfirm.backend.get_uf2_filename(board_id, version, language)
|
|
32
26
|
uf2_folder = get_board_folder(board_id)
|
|
33
27
|
return uf2_folder / file
|
|
34
28
|
|
|
@@ -46,31 +40,33 @@ def is_downloaded(board_id: str, version: str, language: str = "en_US") -> bool:
|
|
|
46
40
|
|
|
47
41
|
def download_uf2(board_id: str, version: str, language: str = "en_US") -> None:
|
|
48
42
|
"""Download a version of CircuitPython for a specific board."""
|
|
49
|
-
file = get_uf2_filename(board_id, version, language=language)
|
|
43
|
+
file = circfirm.backend.get_uf2_filename(board_id, version, language=language)
|
|
50
44
|
uf2_file = get_uf2_filepath(board_id, version, language=language)
|
|
51
45
|
url = f"https://downloads.circuitpython.org/bin/{board_id}/{language}/{file}"
|
|
52
46
|
response = requests.get(url)
|
|
53
47
|
|
|
54
48
|
SUCCESS = 200
|
|
55
49
|
if response.status_code != SUCCESS:
|
|
56
|
-
raise ConnectionError(
|
|
50
|
+
raise ConnectionError(
|
|
51
|
+
f"Could not download the specified UF2 file:\n{url}\nAre the board ID, version, and language correct?"
|
|
52
|
+
)
|
|
57
53
|
|
|
58
54
|
uf2_file.parent.mkdir(parents=True, exist_ok=True)
|
|
59
55
|
with open(uf2_file, mode="wb") as uf2file:
|
|
60
56
|
uf2file.write(response.content)
|
|
61
57
|
|
|
62
58
|
|
|
63
|
-
def get_sorted_boards(board_id: Optional[str]) ->
|
|
59
|
+
def get_sorted_boards(board_id: Optional[str]) -> dict[str, dict[str, set[str]]]:
|
|
64
60
|
"""Get a sorted collection of boards, versions, and languages."""
|
|
65
|
-
boards:
|
|
61
|
+
boards: dict[str, dict[str, set[str]]] = {}
|
|
66
62
|
for board_folder in sorted(os.listdir(circfirm.UF2_ARCHIVE)):
|
|
67
|
-
versions:
|
|
68
|
-
sorted_versions:
|
|
63
|
+
versions: dict[str, list[str]] = {}
|
|
64
|
+
sorted_versions: dict[str, set[str]] = {}
|
|
69
65
|
if board_id is not None and board_id != board_folder:
|
|
70
66
|
continue
|
|
71
67
|
board_folder_full = get_board_folder(board_folder)
|
|
72
68
|
for item in os.listdir(board_folder_full):
|
|
73
|
-
version, language = parse_firmware_info(item)
|
|
69
|
+
version, language = circfirm.backend.parse_firmware_info(item)
|
|
74
70
|
try:
|
|
75
71
|
version_set = set(versions[version])
|
|
76
72
|
version_set.add(language)
|
|
@@ -83,15 +79,3 @@ def get_sorted_boards(board_id: Optional[str]) -> Dict[str, Dict[str, Set[str]]]
|
|
|
83
79
|
sorted_versions[sorted_version] = versions[sorted_version]
|
|
84
80
|
boards[board_folder] = sorted_versions
|
|
85
81
|
return boards
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
def parse_firmware_info(uf2_filename: str) -> Tuple[str, str]:
|
|
89
|
-
"""Get firmware info."""
|
|
90
|
-
regex_match = re.match(circfirm.backend.FIRMWARE_REGEX, uf2_filename)
|
|
91
|
-
if regex_match is None:
|
|
92
|
-
raise ValueError(
|
|
93
|
-
"Firmware information could not be determined from the filename"
|
|
94
|
-
)
|
|
95
|
-
version = regex_match[3]
|
|
96
|
-
language = regex_match[2]
|
|
97
|
-
return version, language
|
circfirm/backend/device.py
CHANGED
|
@@ -9,7 +9,7 @@ Author(s): Alec Delaney
|
|
|
9
9
|
|
|
10
10
|
import pathlib
|
|
11
11
|
import re
|
|
12
|
-
from typing import Optional
|
|
12
|
+
from typing import Optional
|
|
13
13
|
|
|
14
14
|
import psutil
|
|
15
15
|
|
|
@@ -21,7 +21,7 @@ BOARD_VER_REGEX = (
|
|
|
21
21
|
)
|
|
22
22
|
|
|
23
23
|
|
|
24
|
-
def get_board_info(device_path: str) ->
|
|
24
|
+
def get_board_info(device_path: str) -> tuple[str, str]:
|
|
25
25
|
"""Get the attached CircuitPytho board's name and version."""
|
|
26
26
|
bootout_file = pathlib.Path(device_path) / circfirm.BOOTOUT_FILE
|
|
27
27
|
with open(bootout_file, encoding="utf-8") as infofile:
|
circfirm/backend/github.py
CHANGED
|
@@ -9,7 +9,7 @@ Author(s): Alec Delaney
|
|
|
9
9
|
|
|
10
10
|
import datetime
|
|
11
11
|
import re
|
|
12
|
-
from typing import
|
|
12
|
+
from typing import TypedDict
|
|
13
13
|
|
|
14
14
|
import requests
|
|
15
15
|
|
|
@@ -42,7 +42,7 @@ class GitTreeItem(TypedDict):
|
|
|
42
42
|
url: str
|
|
43
43
|
|
|
44
44
|
|
|
45
|
-
def get_rate_limit() ->
|
|
45
|
+
def get_rate_limit() -> tuple[int, int, datetime.datetime]:
|
|
46
46
|
"""Get the rate limit for the GitHub REST endpoint."""
|
|
47
47
|
response = requests.get(
|
|
48
48
|
url="https://api.github.com/rate_limit",
|
|
@@ -55,7 +55,7 @@ def get_rate_limit() -> Tuple[int, int, datetime.datetime]:
|
|
|
55
55
|
return available, total, reset_time
|
|
56
56
|
|
|
57
57
|
|
|
58
|
-
def get_board_id_list(token: str) ->
|
|
58
|
+
def get_board_id_list(token: str) -> list[str]:
|
|
59
59
|
"""Get a list of CircuitPython boards."""
|
|
60
60
|
boards = set()
|
|
61
61
|
headers = BASE_REQUESTS_HEADERS.copy()
|
|
@@ -69,7 +69,7 @@ def get_board_id_list(token: str) -> List[str]:
|
|
|
69
69
|
headers=headers,
|
|
70
70
|
)
|
|
71
71
|
try:
|
|
72
|
-
tree_items:
|
|
72
|
+
tree_items: list[GitTreeItem] = response.json()["tree"]
|
|
73
73
|
except KeyError as err:
|
|
74
74
|
raise ValueError("Could not parse JSON response, check token") from err
|
|
75
75
|
for tree_item in tree_items:
|
circfirm/backend/s3.py
CHANGED
|
@@ -8,7 +8,7 @@ Author(s): Alec Delaney
|
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
10
|
import re
|
|
11
|
-
from typing import
|
|
11
|
+
from typing import Optional
|
|
12
12
|
|
|
13
13
|
import boto3
|
|
14
14
|
import botocore
|
|
@@ -17,6 +17,7 @@ import packaging.version
|
|
|
17
17
|
from mypy_boto3_s3 import S3ServiceResource
|
|
18
18
|
|
|
19
19
|
import circfirm.backend
|
|
20
|
+
import circfirm.backend.cache
|
|
20
21
|
|
|
21
22
|
S3_CONFIG = botocore.client.Config(signature_version=botocore.UNSIGNED)
|
|
22
23
|
S3_RESOURCE: S3ServiceResource = boto3.resource("s3", config=S3_CONFIG)
|
|
@@ -26,24 +27,25 @@ BUCKET = S3_RESOURCE.Bucket(BUCKET_NAME)
|
|
|
26
27
|
|
|
27
28
|
def get_board_versions(
|
|
28
29
|
board_id: str, language: str = "en_US", *, regex: Optional[str] = None
|
|
29
|
-
) ->
|
|
30
|
+
) -> list[str]:
|
|
30
31
|
"""Get a list of CircuitPython versions for a given board."""
|
|
31
32
|
prefix = f"bin/{board_id}/{language}"
|
|
32
33
|
firmware_regex = circfirm.backend.FIRMWARE_REGEX_PATTERN.replace(
|
|
33
34
|
r"[board]", board_id
|
|
34
35
|
).replace(r"[language]", language)
|
|
35
|
-
version_regex =
|
|
36
|
+
version_regex = circfirm.backend._VALID_VERSIONS_CAPTURE
|
|
36
37
|
firmware_regex = firmware_regex.replace(r"[version]", version_regex)
|
|
37
38
|
s3_objects = BUCKET.objects.filter(Prefix=prefix)
|
|
38
39
|
versions = set()
|
|
39
40
|
for s3_object in s3_objects:
|
|
40
41
|
result = re.match(f"{prefix}/{firmware_regex}", s3_object.key)
|
|
41
42
|
if result:
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
43
|
+
if regex:
|
|
44
|
+
firmware_filename = s3_object.key.split("/")[-1]
|
|
45
|
+
version, _ = circfirm.backend.parse_firmware_info(firmware_filename)
|
|
46
|
+
if not re.match(regex, version):
|
|
47
|
+
continue
|
|
48
|
+
versions.add(result[1])
|
|
47
49
|
return sorted(versions, key=packaging.version.Version, reverse=True)
|
|
48
50
|
|
|
49
51
|
|
circfirm/cli/__init__.py
CHANGED
|
@@ -13,7 +13,8 @@ import pkgutil
|
|
|
13
13
|
import shutil
|
|
14
14
|
import sys
|
|
15
15
|
import time
|
|
16
|
-
from
|
|
16
|
+
from collections.abc import Iterable
|
|
17
|
+
from typing import Any, Callable, Optional, TypeVar
|
|
17
18
|
|
|
18
19
|
import click
|
|
19
20
|
import click_spinner
|
|
@@ -43,8 +44,11 @@ def maybe_support(msg: str) -> None:
|
|
|
43
44
|
|
|
44
45
|
|
|
45
46
|
def get_board_id(
|
|
46
|
-
circuitpy: Optional[str],
|
|
47
|
-
|
|
47
|
+
circuitpy: Optional[str],
|
|
48
|
+
bootloader: Optional[str],
|
|
49
|
+
board: Optional[str],
|
|
50
|
+
timeout: int = -1,
|
|
51
|
+
) -> tuple[str, str]:
|
|
48
52
|
"""Get the board ID of a device via CLI."""
|
|
49
53
|
if not board:
|
|
50
54
|
if not circuitpy and bootloader:
|
|
@@ -56,12 +60,22 @@ def get_board_id(
|
|
|
56
60
|
board = circfirm.backend.device.get_board_info(circuitpy)[0]
|
|
57
61
|
|
|
58
62
|
click.echo("Board ID detected, please switch the device to bootloader mode.")
|
|
63
|
+
if timeout == -1:
|
|
64
|
+
skip_timeout = True
|
|
65
|
+
else:
|
|
66
|
+
skip_timeout = False
|
|
67
|
+
start_time = time.time()
|
|
68
|
+
|
|
59
69
|
while not (bootloader := circfirm.backend.device.find_bootloader()):
|
|
60
|
-
time.
|
|
70
|
+
if not skip_timeout and time.time() >= start_time + timeout:
|
|
71
|
+
raise OSError(
|
|
72
|
+
"Bootloader mode device not found within the timeout period"
|
|
73
|
+
)
|
|
74
|
+
time.sleep(0.05)
|
|
61
75
|
return bootloader, board
|
|
62
76
|
|
|
63
77
|
|
|
64
|
-
def get_connection_status() ->
|
|
78
|
+
def get_connection_status() -> tuple[Optional[str], Optional[str]]:
|
|
65
79
|
"""Get the status of a connectted CircuitPython device as a CIRCUITPY and bootloader location."""
|
|
66
80
|
circuitpy = circfirm.backend.device.find_circuitpy()
|
|
67
81
|
bootloader = circfirm.backend.device.find_bootloader()
|
|
@@ -108,14 +122,15 @@ def copy_cache_firmware(
|
|
|
108
122
|
announce_and_await(
|
|
109
123
|
f"Copying UF2 to {board}", shutil.copyfile, args=(uf2file, uf2_path)
|
|
110
124
|
)
|
|
111
|
-
click.echo("
|
|
125
|
+
click.echo(f"CircuitPython version now upgraded to {version}")
|
|
126
|
+
click.echo("Device should reboot momentarily")
|
|
112
127
|
|
|
113
128
|
|
|
114
129
|
def announce_and_await(
|
|
115
130
|
msg: str,
|
|
116
131
|
func: Callable[..., _T],
|
|
117
132
|
args: Iterable = (),
|
|
118
|
-
kwargs: Optional[
|
|
133
|
+
kwargs: Optional[dict[str, Any]] = None,
|
|
119
134
|
*,
|
|
120
135
|
use_spinner: bool = True,
|
|
121
136
|
) -> _T:
|
|
@@ -140,7 +155,7 @@ def announce_and_await(
|
|
|
140
155
|
raise err
|
|
141
156
|
|
|
142
157
|
|
|
143
|
-
def get_settings() ->
|
|
158
|
+
def get_settings() -> dict[str, Any]:
|
|
144
159
|
"""Get the contents of the settings file."""
|
|
145
160
|
with open(circfirm.SETTINGS_FILE, encoding="utf-8") as yamlfile:
|
|
146
161
|
return yaml.safe_load(yamlfile)
|
circfirm/cli/cache.py
CHANGED
|
@@ -9,14 +9,15 @@ Author(s): Alec Delaney
|
|
|
9
9
|
|
|
10
10
|
import os
|
|
11
11
|
import pathlib
|
|
12
|
+
import re
|
|
12
13
|
import shutil
|
|
13
|
-
import sys
|
|
14
14
|
from typing import Optional
|
|
15
15
|
|
|
16
16
|
import click
|
|
17
17
|
|
|
18
18
|
import circfirm
|
|
19
19
|
import circfirm.backend.cache
|
|
20
|
+
import circfirm.backend.s3
|
|
20
21
|
import circfirm.cli
|
|
21
22
|
import circfirm.startup
|
|
22
23
|
|
|
@@ -30,8 +31,18 @@ def cli():
|
|
|
30
31
|
@click.option("-b", "--board-id", default=None, help="CircuitPython board ID")
|
|
31
32
|
@click.option("-v", "--version", default=None, help="CircuitPython version")
|
|
32
33
|
@click.option("-l", "--language", default=None, help="CircuitPython language/locale")
|
|
33
|
-
|
|
34
|
-
|
|
34
|
+
@click.option(
|
|
35
|
+
"-r",
|
|
36
|
+
"--regex",
|
|
37
|
+
is_flag=True,
|
|
38
|
+
default=False,
|
|
39
|
+
help="The board ID, version, and language options represent regex patterns",
|
|
40
|
+
)
|
|
41
|
+
def clear( # noqa: PLR0913
|
|
42
|
+
board_id: Optional[str],
|
|
43
|
+
version: Optional[str],
|
|
44
|
+
language: Optional[str],
|
|
45
|
+
regex: bool,
|
|
35
46
|
) -> None:
|
|
36
47
|
"""Clear the cache, either entirely or for a specific board/version."""
|
|
37
48
|
if board_id is None and version is None and language is None:
|
|
@@ -40,13 +51,33 @@ def clear(
|
|
|
40
51
|
click.echo("Cache cleared!")
|
|
41
52
|
return
|
|
42
53
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
54
|
+
if regex:
|
|
55
|
+
glob_pattern = "*-*-*-*"
|
|
56
|
+
else:
|
|
57
|
+
glob_pattern = "*-*" if board_id is None else f"*-{board_id}"
|
|
58
|
+
language_pattern = "-*" if language is None else f"-{language}"
|
|
59
|
+
glob_pattern += language_pattern
|
|
60
|
+
version_pattern = "-*" if version is None else f"-{version}.uf2"
|
|
61
|
+
glob_pattern += version_pattern
|
|
62
|
+
|
|
48
63
|
matching_files = pathlib.Path(circfirm.UF2_ARCHIVE).rglob(glob_pattern)
|
|
64
|
+
|
|
49
65
|
for matching_file in matching_files:
|
|
66
|
+
if regex:
|
|
67
|
+
board_id = ".*" if board_id is None else board_id
|
|
68
|
+
version = ".*" if version is None else version
|
|
69
|
+
language = ".*" if language is None else language
|
|
70
|
+
|
|
71
|
+
current_board_id = matching_file.parent.name
|
|
72
|
+
current_version, current_language = circfirm.backend.parse_firmware_info(
|
|
73
|
+
matching_file.name
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
board_id_matches = re.search(board_id, current_board_id)
|
|
77
|
+
version_matches = re.match(version, current_version)
|
|
78
|
+
language_matches = re.match(language, current_language)
|
|
79
|
+
if not all([board_id_matches, version_matches, language_matches]):
|
|
80
|
+
continue
|
|
50
81
|
matching_file.unlink()
|
|
51
82
|
|
|
52
83
|
# Delete board folder if empty
|
|
@@ -88,7 +119,7 @@ def cache_list(board_id: Optional[str]) -> None:
|
|
|
88
119
|
@click.argument("version")
|
|
89
120
|
@click.option("-l", "--language", default="en_US", help="CircuitPython language/locale")
|
|
90
121
|
def cache_save(board_id: str, version: str, language: str) -> None:
|
|
91
|
-
"""
|
|
122
|
+
"""Download a version of CircuitPython to the cache."""
|
|
92
123
|
try:
|
|
93
124
|
circfirm.cli.announce_and_await(
|
|
94
125
|
f"Caching firmware version {version} for {board_id}",
|
|
@@ -97,3 +128,28 @@ def cache_save(board_id: str, version: str, language: str) -> None:
|
|
|
97
128
|
)
|
|
98
129
|
except ConnectionError as err:
|
|
99
130
|
raise click.exceptions.ClickException(err.args[0])
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
@cli.command(name="latest")
|
|
134
|
+
@click.argument("board-id")
|
|
135
|
+
@click.option("-l", "--language", default="en_US", help="CircuitPython language/locale")
|
|
136
|
+
@click.option(
|
|
137
|
+
"-p",
|
|
138
|
+
"--pre-release",
|
|
139
|
+
is_flag=True,
|
|
140
|
+
default=False,
|
|
141
|
+
help="Whether pre-release versions should be considered",
|
|
142
|
+
)
|
|
143
|
+
def cache_latest(board_id: str, language: str, pre_release: bool) -> None:
|
|
144
|
+
"""Download the latest version of CircuitPython to the cache."""
|
|
145
|
+
try:
|
|
146
|
+
version = circfirm.backend.s3.get_latest_board_version(
|
|
147
|
+
board_id, language, pre_release
|
|
148
|
+
)
|
|
149
|
+
circfirm.cli.announce_and_await(
|
|
150
|
+
f"Caching firmware version {version} for {board_id}",
|
|
151
|
+
circfirm.backend.cache.download_uf2,
|
|
152
|
+
args=(board_id, version, language),
|
|
153
|
+
)
|
|
154
|
+
except ConnectionError as err:
|
|
155
|
+
raise click.exceptions.ClickException(err.args[0])
|
circfirm/cli/current.py
CHANGED
|
@@ -7,15 +7,13 @@
|
|
|
7
7
|
Author(s): Alec Delaney
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
|
-
from typing import Tuple
|
|
11
|
-
|
|
12
10
|
import click
|
|
13
11
|
|
|
14
12
|
import circfirm.backend.device
|
|
15
13
|
import circfirm.cli
|
|
16
14
|
|
|
17
15
|
|
|
18
|
-
def get_board_info() ->
|
|
16
|
+
def get_board_info() -> tuple[str, str]:
|
|
19
17
|
"""Get board info via the CLI."""
|
|
20
18
|
circuitpy, _ = circfirm.cli.get_connection_status()
|
|
21
19
|
if not circuitpy:
|
circfirm/cli/detect.py
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: 2024 Alec Delaney, for Adafruit Industries
|
|
2
|
+
#
|
|
3
|
+
# SPDX-License-Identifier: MIT
|
|
4
|
+
|
|
5
|
+
"""CLI functionality for the detect subcommand.
|
|
6
|
+
|
|
7
|
+
Author(s): Alec Delaney
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
import click
|
|
12
|
+
|
|
13
|
+
import circfirm.backend.device
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@click.group()
|
|
17
|
+
def cli() -> None:
|
|
18
|
+
"""Detect connected CircuitPython boards."""
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@cli.command(name="circuitpy")
|
|
22
|
+
def detect_circuitpy() -> None:
|
|
23
|
+
"""Detect a connected board in CIRCUITPY or equivalent mode."""
|
|
24
|
+
circuitpy = circfirm.backend.device.find_circuitpy()
|
|
25
|
+
if not circuitpy:
|
|
26
|
+
click.echo("No board connected in CIRCUITPY or equivalent mode")
|
|
27
|
+
return
|
|
28
|
+
click.echo(circuitpy)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@cli.command(name="bootloader")
|
|
32
|
+
def detect_bootloader() -> None:
|
|
33
|
+
"""Detect a connected board in bootloader mode."""
|
|
34
|
+
bootloader = circfirm.backend.device.find_bootloader()
|
|
35
|
+
if not bootloader:
|
|
36
|
+
click.echo("No board connected in bootloader mode")
|
|
37
|
+
return
|
|
38
|
+
click.echo(bootloader)
|
circfirm/cli/install.py
CHANGED
|
@@ -23,10 +23,21 @@ import circfirm.cli
|
|
|
23
23
|
default=None,
|
|
24
24
|
help="Assume the given board ID (and connect in bootloader mode)",
|
|
25
25
|
)
|
|
26
|
-
|
|
26
|
+
@click.option(
|
|
27
|
+
"-t",
|
|
28
|
+
"--timeout",
|
|
29
|
+
default=-1,
|
|
30
|
+
help="Set a timeout in seconds for the switch to bootloader mode",
|
|
31
|
+
)
|
|
32
|
+
def cli(version: str, language: str, board_id: Optional[str], timeout: int) -> None:
|
|
27
33
|
"""Install the specified version of CircuitPython."""
|
|
28
34
|
circuitpy, bootloader = circfirm.cli.get_connection_status()
|
|
29
|
-
|
|
35
|
+
try:
|
|
36
|
+
bootloader, board_id = circfirm.cli.get_board_id(
|
|
37
|
+
circuitpy, bootloader, board_id, timeout
|
|
38
|
+
)
|
|
39
|
+
except OSError as err:
|
|
40
|
+
raise click.ClickException(err.args[0])
|
|
30
41
|
circfirm.cli.ensure_bootloader_mode(bootloader)
|
|
31
42
|
circfirm.cli.download_if_needed(board_id, version, language)
|
|
32
43
|
circfirm.cli.copy_cache_firmware(board_id, version, language, bootloader)
|
circfirm/cli/query.py
CHANGED
|
@@ -24,7 +24,9 @@ def cli():
|
|
|
24
24
|
|
|
25
25
|
|
|
26
26
|
@cli.command(name="board-ids")
|
|
27
|
-
@click.option(
|
|
27
|
+
@click.option(
|
|
28
|
+
"-r", "--regex", default=".*", help="Regex pattern to use for board IDs (search)"
|
|
29
|
+
)
|
|
28
30
|
def query_board_ids(regex: str) -> None:
|
|
29
31
|
"""Query the local CircuitPython board list."""
|
|
30
32
|
settings = circfirm.cli.get_settings()
|
|
@@ -33,7 +35,7 @@ def query_board_ids(regex: str) -> None:
|
|
|
33
35
|
circfirm.cli.maybe_support(
|
|
34
36
|
"Boards list will now be synchronized with the git repository."
|
|
35
37
|
)
|
|
36
|
-
if not gh_token:
|
|
38
|
+
if not gh_token: # pragma: no cover
|
|
37
39
|
circfirm.cli.maybe_support(
|
|
38
40
|
"Please note that this operation can only be performed 60 times per hour due to GitHub rate limiting."
|
|
39
41
|
)
|
|
@@ -62,7 +64,9 @@ def query_board_ids(regex: str) -> None:
|
|
|
62
64
|
@cli.command(name="versions")
|
|
63
65
|
@click.argument("board-id")
|
|
64
66
|
@click.option("-l", "--language", default="en_US", help="CircuitPython language/locale")
|
|
65
|
-
@click.option(
|
|
67
|
+
@click.option(
|
|
68
|
+
"-r", "--regex", default=".*", help="Regex pattern to use for versions (match)"
|
|
69
|
+
)
|
|
66
70
|
def query_versions(board_id: str, language: str, regex: str) -> None:
|
|
67
71
|
"""Query the CircuitPython versions available for a board."""
|
|
68
72
|
versions = circfirm.backend.s3.get_board_versions(board_id, language, regex=regex)
|
circfirm/cli/update.py
CHANGED
|
@@ -25,6 +25,12 @@ import circfirm.cli.install
|
|
|
25
25
|
help="Assume the given board ID (and connect in bootloader mode)",
|
|
26
26
|
)
|
|
27
27
|
@click.option("-l", "--language", default="en_US", help="CircuitPython langauge/locale")
|
|
28
|
+
@click.option(
|
|
29
|
+
"-t",
|
|
30
|
+
"--timeout",
|
|
31
|
+
default=-1,
|
|
32
|
+
help="Set a timeout in seconds for the switch to bootloader mode",
|
|
33
|
+
)
|
|
28
34
|
@click.option(
|
|
29
35
|
"-p",
|
|
30
36
|
"--pre-release",
|
|
@@ -32,7 +38,28 @@ import circfirm.cli.install
|
|
|
32
38
|
default=False,
|
|
33
39
|
help="Whether pre-release versions should be considered",
|
|
34
40
|
)
|
|
35
|
-
|
|
41
|
+
@click.option(
|
|
42
|
+
"-y",
|
|
43
|
+
"--limit-to-minor",
|
|
44
|
+
is_flag=True,
|
|
45
|
+
default=False,
|
|
46
|
+
help="Upgrade up to minor version updates",
|
|
47
|
+
)
|
|
48
|
+
@click.option(
|
|
49
|
+
"-z",
|
|
50
|
+
"--limit-to-patch",
|
|
51
|
+
is_flag=True,
|
|
52
|
+
default=False,
|
|
53
|
+
help="Upgrade up to patch version updates",
|
|
54
|
+
)
|
|
55
|
+
def cli( # noqa: PLR0913
|
|
56
|
+
board_id: Optional[str],
|
|
57
|
+
language: str,
|
|
58
|
+
timeout: int,
|
|
59
|
+
pre_release: bool,
|
|
60
|
+
limit_to_minor: bool,
|
|
61
|
+
limit_to_patch: bool,
|
|
62
|
+
) -> None:
|
|
36
63
|
"""Update a connected board to the latest CircuitPython version."""
|
|
37
64
|
circuitpy, bootloader = circfirm.cli.get_connection_status()
|
|
38
65
|
if circuitpy:
|
|
@@ -45,11 +72,43 @@ def cli(board_id: Optional[str], language: str, pre_release: bool) -> None:
|
|
|
45
72
|
"The latest version will be installed regardless of the currently installed version."
|
|
46
73
|
)
|
|
47
74
|
current_version = "0.0.0"
|
|
48
|
-
|
|
75
|
+
try:
|
|
76
|
+
bootloader, board_id = circfirm.cli.get_board_id(
|
|
77
|
+
circuitpy, bootloader, board_id, timeout
|
|
78
|
+
)
|
|
79
|
+
except OSError as err:
|
|
80
|
+
raise click.ClickException(err.args[0])
|
|
81
|
+
|
|
82
|
+
new_versions = circfirm.backend.s3.get_board_versions(board_id, language)
|
|
83
|
+
|
|
84
|
+
if not pre_release:
|
|
85
|
+
new_versions = [
|
|
86
|
+
version
|
|
87
|
+
for version in new_versions
|
|
88
|
+
if not packaging.version.Version(version).is_prerelease
|
|
89
|
+
]
|
|
90
|
+
|
|
91
|
+
if limit_to_minor or limit_to_patch:
|
|
92
|
+
new_versions = [
|
|
93
|
+
version
|
|
94
|
+
for version in new_versions
|
|
95
|
+
if packaging.version.Version(version).major
|
|
96
|
+
<= packaging.version.Version(current_version).major
|
|
97
|
+
]
|
|
98
|
+
if limit_to_patch:
|
|
99
|
+
new_versions = [
|
|
100
|
+
version
|
|
101
|
+
for version in new_versions
|
|
102
|
+
if packaging.version.Version(version).minor
|
|
103
|
+
<= packaging.version.Version(current_version).minor
|
|
104
|
+
]
|
|
105
|
+
|
|
106
|
+
if not new_versions:
|
|
107
|
+
raise click.ClickException(
|
|
108
|
+
"No versions exist that meet the given update criteria"
|
|
109
|
+
)
|
|
49
110
|
|
|
50
|
-
new_version =
|
|
51
|
-
board_id, language, pre_release
|
|
52
|
-
)
|
|
111
|
+
new_version = new_versions[0]
|
|
53
112
|
if packaging.version.Version(current_version) >= packaging.version.Version(
|
|
54
113
|
new_version
|
|
55
114
|
):
|
circfirm/startup.py
CHANGED
|
@@ -10,13 +10,12 @@ Author(s): Alec Delaney
|
|
|
10
10
|
import os
|
|
11
11
|
import pathlib
|
|
12
12
|
import shutil
|
|
13
|
-
from typing import List, Tuple
|
|
14
13
|
|
|
15
14
|
import click
|
|
16
15
|
|
|
17
|
-
FOLDER_LIST:
|
|
18
|
-
FILE_LIST:
|
|
19
|
-
TEMPLATE_LIST:
|
|
16
|
+
FOLDER_LIST: list[str] = []
|
|
17
|
+
FILE_LIST: list[str] = []
|
|
18
|
+
TEMPLATE_LIST: list[tuple[str, str]] = []
|
|
20
19
|
|
|
21
20
|
|
|
22
21
|
def specify_app_dir(app_name: str) -> str:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
2
|
Name: circfirm
|
|
3
|
-
Version:
|
|
3
|
+
Version: 4.0.0
|
|
4
4
|
Summary: CLI tool for install firmware for CircuitPython boards
|
|
5
5
|
Author-email: Alec Delaney <tekktrik@gmail.com>
|
|
6
6
|
License: MIT
|
|
@@ -15,35 +15,37 @@ Classifier: License :: OSI Approved :: MIT License
|
|
|
15
15
|
Classifier: Development Status :: 5 - Production/Stable
|
|
16
16
|
Classifier: Environment :: Console
|
|
17
17
|
Classifier: Natural Language :: English
|
|
18
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
19
18
|
Classifier: Programming Language :: Python :: 3.9
|
|
20
19
|
Classifier: Programming Language :: Python :: 3.10
|
|
21
20
|
Classifier: Programming Language :: Python :: 3.11
|
|
22
21
|
Classifier: Programming Language :: Python :: 3.12
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
23
23
|
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
24
24
|
Classifier: Operating System :: Unix
|
|
25
25
|
Classifier: Operating System :: Microsoft :: Windows
|
|
26
26
|
Classifier: Operating System :: MacOS
|
|
27
27
|
Classifier: Typing :: Typed
|
|
28
|
-
Requires-Python: >=3.
|
|
28
|
+
Requires-Python: >=3.9.0
|
|
29
29
|
Description-Content-Type: text/x-rst
|
|
30
30
|
License-File: LICENSE
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
Requires-Dist:
|
|
34
|
-
Requires-Dist:
|
|
35
|
-
Requires-Dist:
|
|
36
|
-
Requires-Dist:
|
|
37
|
-
Requires-Dist:
|
|
38
|
-
Requires-Dist:
|
|
31
|
+
License-File: NOTICE
|
|
32
|
+
License-File: NOTICE.license
|
|
33
|
+
Requires-Dist: boto3~=1.35
|
|
34
|
+
Requires-Dist: click~=8.1
|
|
35
|
+
Requires-Dist: click-spinner~=0.1
|
|
36
|
+
Requires-Dist: packaging~=24.2
|
|
37
|
+
Requires-Dist: psutil~=6.1
|
|
38
|
+
Requires-Dist: pyyaml~=6.0
|
|
39
|
+
Requires-Dist: requests~=2.32
|
|
40
|
+
Requires-Dist: boto3-stubs[essential]~=1.35
|
|
39
41
|
Provides-Extra: dev
|
|
40
|
-
Requires-Dist: build
|
|
41
|
-
Requires-Dist: coverage
|
|
42
|
-
Requires-Dist: pre-commit
|
|
43
|
-
Requires-Dist: pytest
|
|
44
|
-
Requires-Dist: sphinx
|
|
45
|
-
Requires-Dist: sphinx-tabs
|
|
46
|
-
Requires-Dist: sphinx-rtd-theme
|
|
42
|
+
Requires-Dist: build~=1.2; extra == "dev"
|
|
43
|
+
Requires-Dist: coverage~=7.6; extra == "dev"
|
|
44
|
+
Requires-Dist: pre-commit~=4.0; extra == "dev"
|
|
45
|
+
Requires-Dist: pytest~=8.3; extra == "dev"
|
|
46
|
+
Requires-Dist: sphinx~=7.4; extra == "dev"
|
|
47
|
+
Requires-Dist: sphinx-tabs~=3.4; extra == "dev"
|
|
48
|
+
Requires-Dist: sphinx-rtd-theme~=3.0; extra == "dev"
|
|
47
49
|
|
|
48
50
|
..
|
|
49
51
|
SPDX-FileCopyrightText: 2022 Alec Delaney, for Adafruit Industries
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
Required Dependencies
|
|
2
|
+
^^^^^^^^^^^^^^^^^^^^^
|
|
3
|
+
|
|
4
|
+
This software interacts with the CircuitPython AWS S3 bucket using boto3 by
|
|
5
|
+
Amazon.com Inc or its affiliates (https://github.com/boto/boto3) [Apache-2.0 license].
|
|
6
|
+
|
|
7
|
+
This software uses type annotations for boto3 provided by boto3-stubs by Vlad
|
|
8
|
+
Emelinaov and maintained by youtype (https://github.com/youtype/mypy_boto3_builder)
|
|
9
|
+
[MIT license].
|
|
10
|
+
|
|
11
|
+
This software creates its command line interface using click by the pallets
|
|
12
|
+
project (https://palletsprojects.com/p/click/) [BSD-3-Clause license].
|
|
13
|
+
|
|
14
|
+
This software uses a CLI spinner add-on click-spinner to clink to display loading
|
|
15
|
+
animation created by Yoav Ram (https://github.com/click-contrib/click-spinner)
|
|
16
|
+
[MIT license].
|
|
17
|
+
|
|
18
|
+
This software compares and orders CircuitPython versions according to PEP 440
|
|
19
|
+
using the packaging library by Donald Stufft and maintained by the Python Packaging
|
|
20
|
+
Authority (https://packaging.pypa.io/en/stable/) [Apache-2.0 or BSD-2-Clause
|
|
21
|
+
licenses].
|
|
22
|
+
|
|
23
|
+
This software discovers connected CircuitPython boards using the system utilization
|
|
24
|
+
features of psutil, created by Jay Loden, Dave Daeschler, and Giampaolo Rodola
|
|
25
|
+
(https://github.com/giampaolo/psutil) [BSD-3-Clause license].
|
|
26
|
+
|
|
27
|
+
This software reads and modifies the YAML configuration settings using PyYAML,
|
|
28
|
+
created by Kirill Simonov and Ingy dot Net, and maintained by the YAML community
|
|
29
|
+
(https://pyyaml.org/) [MIT license].
|
|
30
|
+
|
|
31
|
+
requests~=2.31
|
|
32
|
+
This software makes HTTP requests, such as communicating with the CircuitPython
|
|
33
|
+
GitHub repository and downloading CircuitPython firmware files, using requests,
|
|
34
|
+
created by Kenneth Reitz and maintained by the Python Software Foundation
|
|
35
|
+
(https://requests.readthedocs.io/en/latest/) [Apache-2.0 license].
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
Development Dependencies
|
|
39
|
+
^^^^^^^^^^^^^^^^^^^^^^^^
|
|
40
|
+
|
|
41
|
+
This software uses the build package for its build frontend, created by Filipe Lains
|
|
42
|
+
and maintained by the Python Packaging Authority (https://build.pypa.io/en/stable/)
|
|
43
|
+
[MIT license].
|
|
44
|
+
|
|
45
|
+
This software measures code coverage metrics using Coverage.py, authored by and
|
|
46
|
+
copyright Ned Batchelder (https://github.com/nedbat/coveragepy) [Apache-2.0 license].
|
|
47
|
+
|
|
48
|
+
This software manages code checks such as linting and formatting using pre-commit,
|
|
49
|
+
copyright Anythony Sottile and Ken Struys and (https://github.com/pre-commit/pre-commit)
|
|
50
|
+
[MIT license].
|
|
51
|
+
|
|
52
|
+
This software performs software tests using pytest, copyright Holger Krekel and others
|
|
53
|
+
(https://docs.pytest.org/en/latest/) [MIT license].
|
|
54
|
+
|
|
55
|
+
Ths software creates its documentation using Sphinx, copyright the Sphinx team
|
|
56
|
+
(https://www.sphinx-doc.org/en/master/) [BSD license].
|
|
57
|
+
|
|
58
|
+
This software uses tabs within its documentation provided by sphinx-tabs, copyright
|
|
59
|
+
djungelorm (https://github.com/executablebooks/sphinx-tabs) [MIT license].
|
|
60
|
+
|
|
61
|
+
sphinx-rtd-theme~=1.0
|
|
62
|
+
This software displays its documentation using the ReadTheDocs Sphinx theme, copyright Dave
|
|
63
|
+
Snider, Read the Docs, Inc. & contributors (https://sphinx-rtd-theme.readthedocs.io/en/stable/)
|
|
64
|
+
[MIT license].
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
circfirm/__init__.py,sha256=Uk6XRo7aFQcwUOMfst4GVt8tuXbMRnDIsPRrWeBtxLQ,719
|
|
2
|
+
circfirm/py.typed,sha256=F2H7kdQErBIQUjr5WG2gdwj35iaUHi4Eyc2hovaLTMA,97
|
|
3
|
+
circfirm/startup.py,sha256=HoeMpISAJswKTFn-sHhtoCD5yISHW5z8ZPW7WgSr2xg,1946
|
|
4
|
+
circfirm/backend/__init__.py,sha256=fzxooHsys60iPrj6q6l-t3m2iGAC6FEIinP1UFgGHKE,1858
|
|
5
|
+
circfirm/backend/cache.py,sha256=cxd_Za68SotnQ4td9R3-_F20Rci5BMVO1LgD0CVpKRo,2931
|
|
6
|
+
circfirm/backend/device.py,sha256=t_g4iPJ4C2Qg5xKUr1AI6XPu5-ULTDZ4e3qAZVSfJs8,1776
|
|
7
|
+
circfirm/backend/github.py,sha256=ePHXTCxqGcJOyVsutdbsL80MaxIYBdNIV9Yzz8aG4Yg,2090
|
|
8
|
+
circfirm/backend/s3.py,sha256=eTwCD79E8TCia2kPcj1-p7qmIvTt2vqQC_K7mHKWeyk,2175
|
|
9
|
+
circfirm/cli/__init__.py,sha256=TBJxvMqzTXQNEv2jKTZHqxeTTvGoQ9sz4o4R91Qe4Kg,6454
|
|
10
|
+
circfirm/cli/about.py,sha256=_QNqXtbyerlxewPVieCdOvQ6BZWqdgvncBIzj39zpCU,340
|
|
11
|
+
circfirm/cli/cache.py,sha256=z1DtWFusGAeUnzFTl0dcxyko7eOQmJUyR1x3orfEWk8,5222
|
|
12
|
+
circfirm/cli/config.py,sha256=9ye3jQhgXaOMAXIlJWHp8PhBEtjUTJSL9szTXCQU2Cg,3122
|
|
13
|
+
circfirm/cli/current.py,sha256=3kjvAQ5oFuXxey8RtU6T5GJPnTmIy2saEpyYoqVOTU8,1034
|
|
14
|
+
circfirm/cli/detect.py,sha256=hXOIFjQAylx5kDT1chU2HJXqIv14scDJheO5-XUioeI,944
|
|
15
|
+
circfirm/cli/install.py,sha256=z0pT7J_n4A3dJn-ocZk-uYcKkOZ3iUHbId-f-k97cg0,1253
|
|
16
|
+
circfirm/cli/query.py,sha256=628UQhQxUMoTnKwPJhhL0ZjmKR_zNJTFgq-PzV-SPlM,2959
|
|
17
|
+
circfirm/cli/update.py,sha256=S25Cty4HXy71hQvAXbxP_JNN_2JgcvXxfocte0rzMWE,3433
|
|
18
|
+
circfirm/templates/settings.yaml,sha256=SM1zjXvZg1jikzn9TjgOufl0Pn9KBbrw8sIcJ01BRhQ,69
|
|
19
|
+
circfirm/templates/settings.yaml.license,sha256=F2H7kdQErBIQUjr5WG2gdwj35iaUHi4Eyc2hovaLTMA,97
|
|
20
|
+
circfirm-4.0.0.dist-info/LICENSE,sha256=6pPP6gJ00tqCkxg5gABHDwWUiXZ_mBzH94xxPOqwGj4,1069
|
|
21
|
+
circfirm-4.0.0.dist-info/METADATA,sha256=vedVLmckngt1DSxN0IUD9q5c_NujIfxih9JTUg0ktok,4678
|
|
22
|
+
circfirm-4.0.0.dist-info/NOTICE,sha256=-iTImDmAffekkp_kj8De0_pvckHvXAHLPAJTWOgQsiw,2956
|
|
23
|
+
circfirm-4.0.0.dist-info/NOTICE.license,sha256=FIvC5TnLXwPBj-dgEV4FBwAlJxzMXhgl50TXLv2d88o,96
|
|
24
|
+
circfirm-4.0.0.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
|
25
|
+
circfirm-4.0.0.dist-info/entry_points.txt,sha256=33qZTmSuXz8dgi29yjSyb8VsEA8HyhWuhn2MNcjatGQ,46
|
|
26
|
+
circfirm-4.0.0.dist-info/top_level.txt,sha256=qA2407wap3My6jGA5uchH2JjUM7qn73oBPwALN7GR5k,9
|
|
27
|
+
circfirm-4.0.0.dist-info/RECORD,,
|
circfirm-3.0.0.dist-info/RECORD
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
circfirm/__init__.py,sha256=Uk6XRo7aFQcwUOMfst4GVt8tuXbMRnDIsPRrWeBtxLQ,719
|
|
2
|
-
circfirm/py.typed,sha256=F2H7kdQErBIQUjr5WG2gdwj35iaUHi4Eyc2hovaLTMA,97
|
|
3
|
-
circfirm/startup.py,sha256=eorElSnwGkW7jb87t0J7y7JAtg1yyeSD2xNqDjXPI6I,1977
|
|
4
|
-
circfirm/backend/__init__.py,sha256=q9z9vk1zumEfhqkW3cPtHVR1ASPXYV3QhkUV53GuPeA,1232
|
|
5
|
-
circfirm/backend/cache.py,sha256=u2UXMwyblP2M5Oc3uZJamYNGFprLGnJ_za22XEWM77Q,3475
|
|
6
|
-
circfirm/backend/device.py,sha256=4K36uMVdH3kGnBBbZ_ApGrwB-G3CtC-aMdH_fP6k7S0,1783
|
|
7
|
-
circfirm/backend/github.py,sha256=vyDeUS76haKMVVOewS4xq1tRHGtHb1SWXP6ippOKtZs,2103
|
|
8
|
-
circfirm/backend/s3.py,sha256=eKf59aqytoK088HsQpqXIEW7rzAtHWOMGKTvMXIIcOk,2080
|
|
9
|
-
circfirm/cli/__init__.py,sha256=8TixyETPfgmk8tzOVJ95sXbUcXIxhLL747MIlynLFps,5993
|
|
10
|
-
circfirm/cli/about.py,sha256=_QNqXtbyerlxewPVieCdOvQ6BZWqdgvncBIzj39zpCU,340
|
|
11
|
-
circfirm/cli/cache.py,sha256=7FazwzQAuZlb-tP7OGVCjI2RI3mee0aJZ986YyvMiHE,3335
|
|
12
|
-
circfirm/cli/config.py,sha256=9ye3jQhgXaOMAXIlJWHp8PhBEtjUTJSL9szTXCQU2Cg,3122
|
|
13
|
-
circfirm/cli/current.py,sha256=ZfMbBVYS5AyVdHb8s0ElvkUX5QuX6_iOrvjVfyAfNGU,1060
|
|
14
|
-
circfirm/cli/install.py,sha256=dJQhZVpNOVcspcPMSyTtZq3BiX_RgXEm9ejaaNWcvXA,989
|
|
15
|
-
circfirm/cli/query.py,sha256=GeQKVoAsnYBqu5ykIgVwbDGgeJtsGvQ_mJ9DVRkYWGw,2910
|
|
16
|
-
circfirm/cli/update.py,sha256=eO950GVruB_NxJpxV3sBuhZyakFRFFkKvXK8hOAy4UU,1983
|
|
17
|
-
circfirm/templates/settings.yaml,sha256=SM1zjXvZg1jikzn9TjgOufl0Pn9KBbrw8sIcJ01BRhQ,69
|
|
18
|
-
circfirm/templates/settings.yaml.license,sha256=F2H7kdQErBIQUjr5WG2gdwj35iaUHi4Eyc2hovaLTMA,97
|
|
19
|
-
circfirm-3.0.0.dist-info/LICENSE,sha256=6pPP6gJ00tqCkxg5gABHDwWUiXZ_mBzH94xxPOqwGj4,1069
|
|
20
|
-
circfirm-3.0.0.dist-info/METADATA,sha256=RRw2f0Fl75_5pEpWO5BdzoF5r_qyP-xaP55OlA2Mscg,4650
|
|
21
|
-
circfirm-3.0.0.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
|
22
|
-
circfirm-3.0.0.dist-info/entry_points.txt,sha256=33qZTmSuXz8dgi29yjSyb8VsEA8HyhWuhn2MNcjatGQ,46
|
|
23
|
-
circfirm-3.0.0.dist-info/top_level.txt,sha256=qA2407wap3My6jGA5uchH2JjUM7qn73oBPwALN7GR5k,9
|
|
24
|
-
circfirm-3.0.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|