circfirm 1.0.1__py3-none-any.whl → 2.0.1__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.py +30 -16
- circfirm/cli.py +29 -19
- {circfirm-1.0.1.dist-info → circfirm-2.0.1.dist-info}/METADATA +1 -1
- circfirm-2.0.1.dist-info/RECORD +10 -0
- circfirm-1.0.1.dist-info/RECORD +0 -10
- {circfirm-1.0.1.dist-info → circfirm-2.0.1.dist-info}/LICENSE +0 -0
- {circfirm-1.0.1.dist-info → circfirm-2.0.1.dist-info}/WHEEL +0 -0
- {circfirm-1.0.1.dist-info → circfirm-2.0.1.dist-info}/entry_points.txt +0 -0
- {circfirm-1.0.1.dist-info → circfirm-2.0.1.dist-info}/top_level.txt +0 -0
circfirm/backend.py
CHANGED
|
@@ -10,6 +10,7 @@ Author(s): Alec Delaney
|
|
|
10
10
|
import enum
|
|
11
11
|
import os
|
|
12
12
|
import pathlib
|
|
13
|
+
import re
|
|
13
14
|
from typing import Dict, List, Optional, Set, Tuple
|
|
14
15
|
|
|
15
16
|
import packaging.version
|
|
@@ -45,6 +46,18 @@ class Language(enum.Enum):
|
|
|
45
46
|
MANDARIN_LATIN_PINYIN = "zh_Latn_pinyin"
|
|
46
47
|
|
|
47
48
|
|
|
49
|
+
_ALL_LANGAGES = [language.value for language in Language]
|
|
50
|
+
_ALL_LANGUAGES_REGEX = "|".join(_ALL_LANGAGES)
|
|
51
|
+
FIRMWARE_REGEX = "-".join(
|
|
52
|
+
[
|
|
53
|
+
r"adafruit-circuitpython-(.*)",
|
|
54
|
+
f"({_ALL_LANGUAGES_REGEX})",
|
|
55
|
+
r"(\d+\.\d+\.\d+(?:-(?:\balpha\b|\bbeta\b)\.\d+)*)\.uf2",
|
|
56
|
+
]
|
|
57
|
+
)
|
|
58
|
+
BOARD_ID_REGEX = r"Board ID:\s*(.*)"
|
|
59
|
+
|
|
60
|
+
|
|
48
61
|
def _find_device(filename: str) -> Optional[str]:
|
|
49
62
|
"""Find a specific connected device."""
|
|
50
63
|
for partition in psutil.disk_partitions():
|
|
@@ -69,19 +82,20 @@ def find_bootloader() -> Optional[str]:
|
|
|
69
82
|
|
|
70
83
|
def get_board_name(device_path: str) -> str:
|
|
71
84
|
"""Get the attached CircuitPython board's name."""
|
|
72
|
-
|
|
73
|
-
with open(
|
|
85
|
+
bootout_file = pathlib.Path(device_path) / circfirm.BOOTOUT_FILE
|
|
86
|
+
with open(bootout_file, encoding="utf-8") as infofile:
|
|
74
87
|
contents = infofile.read()
|
|
75
|
-
|
|
76
|
-
|
|
88
|
+
board_match = re.search(BOARD_ID_REGEX, contents)
|
|
89
|
+
if not board_match:
|
|
90
|
+
raise ValueError("Could not parse the board name from the boot out file")
|
|
91
|
+
return board_match[1]
|
|
77
92
|
|
|
78
93
|
|
|
79
94
|
def download_uf2(board: str, version: str, language: str = "en_US") -> None:
|
|
80
95
|
"""Download a version of CircuitPython for a specific board."""
|
|
81
96
|
file = get_uf2_filename(board, version, language=language)
|
|
82
|
-
board_name = board.replace(" ", "_").lower()
|
|
83
97
|
uf2_file = get_uf2_filepath(board, version, language=language, ensure=True)
|
|
84
|
-
url = f"https://downloads.circuitpython.org/bin/{
|
|
98
|
+
url = f"https://downloads.circuitpython.org/bin/{board}/{language}/{file}"
|
|
85
99
|
response = requests.get(url)
|
|
86
100
|
|
|
87
101
|
SUCCESS = 200
|
|
@@ -105,8 +119,7 @@ def get_uf2_filepath(
|
|
|
105
119
|
) -> pathlib.Path:
|
|
106
120
|
"""Get the path to a downloaded UF2 file."""
|
|
107
121
|
file = get_uf2_filename(board, version, language)
|
|
108
|
-
|
|
109
|
-
uf2_folder = pathlib.Path(circfirm.UF2_ARCHIVE) / board_name
|
|
122
|
+
uf2_folder = pathlib.Path(circfirm.UF2_ARCHIVE) / board
|
|
110
123
|
if ensure:
|
|
111
124
|
circfirm.startup.ensure_dir(uf2_folder)
|
|
112
125
|
return pathlib.Path(uf2_folder) / file
|
|
@@ -114,22 +127,23 @@ def get_uf2_filepath(
|
|
|
114
127
|
|
|
115
128
|
def get_uf2_filename(board: str, version: str, language: str = "en_US") -> str:
|
|
116
129
|
"""Get the structured name for a specific board/version CircuitPython."""
|
|
117
|
-
|
|
118
|
-
return f"adafruit-circuitpython-{board_name}-{language}-{version}.uf2"
|
|
130
|
+
return f"adafruit-circuitpython-{board}-{language}-{version}.uf2"
|
|
119
131
|
|
|
120
132
|
|
|
121
133
|
def get_board_folder(board: str) -> pathlib.Path:
|
|
122
134
|
"""Get the board folder path."""
|
|
123
|
-
|
|
124
|
-
return pathlib.Path(circfirm.UF2_ARCHIVE) / board_name
|
|
135
|
+
return pathlib.Path(circfirm.UF2_ARCHIVE) / board
|
|
125
136
|
|
|
126
137
|
|
|
127
138
|
def get_firmware_info(uf2_filename: str) -> Tuple[str, str]:
|
|
128
139
|
"""Get firmware info."""
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
140
|
+
regex_match = re.match(FIRMWARE_REGEX, uf2_filename)
|
|
141
|
+
if regex_match is None:
|
|
142
|
+
raise ValueError(
|
|
143
|
+
"Firmware information could not be determined from the filename"
|
|
144
|
+
)
|
|
145
|
+
version = regex_match[3]
|
|
146
|
+
language = regex_match[2]
|
|
133
147
|
return version, language
|
|
134
148
|
|
|
135
149
|
|
circfirm/cli.py
CHANGED
|
@@ -11,6 +11,7 @@ import os
|
|
|
11
11
|
import pathlib
|
|
12
12
|
import shutil
|
|
13
13
|
import sys
|
|
14
|
+
import time
|
|
14
15
|
from typing import Optional
|
|
15
16
|
|
|
16
17
|
import click
|
|
@@ -30,21 +31,34 @@ def cli() -> None:
|
|
|
30
31
|
@cli.command()
|
|
31
32
|
@click.argument("version")
|
|
32
33
|
@click.option("-l", "--language", default="en_US", help="CircuitPython language/locale")
|
|
33
|
-
|
|
34
|
+
@click.option("-b", "--board", default=None, help="Assume the given board name")
|
|
35
|
+
def install(version: str, language: str, board: Optional[str]) -> None:
|
|
34
36
|
"""Install the specified version of CircuitPython."""
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
37
|
+
circuitpy = circfirm.backend.find_circuitpy()
|
|
38
|
+
bootloader = circfirm.backend.find_bootloader()
|
|
39
|
+
if not circuitpy and not bootloader:
|
|
40
|
+
click.echo("CircuitPython device not found!")
|
|
41
|
+
click.echo("Check that the device is connected and mounted.")
|
|
42
|
+
sys.exit(1)
|
|
43
|
+
|
|
44
|
+
if not board:
|
|
45
|
+
if not circuitpy and bootloader:
|
|
46
|
+
click.echo("CircuitPython device found, but it is in bootloader mode!")
|
|
47
|
+
click.echo(
|
|
48
|
+
"Please put the device out of bootloader mode, or use the --board option."
|
|
49
|
+
)
|
|
50
|
+
sys.exit(3)
|
|
51
|
+
board = circfirm.backend.get_board_name(circuitpy)
|
|
52
|
+
|
|
53
|
+
click.echo("Board name detected, please switch the device to bootloader mode.")
|
|
54
|
+
while not (bootloader := circfirm.backend.find_bootloader()):
|
|
55
|
+
time.sleep(1)
|
|
56
|
+
|
|
57
|
+
if not bootloader:
|
|
58
|
+
if circfirm.backend.find_circuitpy():
|
|
39
59
|
click.echo("CircuitPython device found, but is not in bootloader mode!")
|
|
40
60
|
click.echo("Please put the device in bootloader mode.")
|
|
41
61
|
sys.exit(2)
|
|
42
|
-
else:
|
|
43
|
-
click.echo("CircuitPython device not found!")
|
|
44
|
-
click.echo("Check that the device is connected and mounted.")
|
|
45
|
-
sys.exit(1)
|
|
46
|
-
|
|
47
|
-
board = circfirm.backend.get_board_name(mount_path)
|
|
48
62
|
|
|
49
63
|
if not circfirm.backend.is_downloaded(board, version, language):
|
|
50
64
|
click.echo("Downloading UF2...")
|
|
@@ -52,7 +66,7 @@ def install(version: str, language: str) -> None:
|
|
|
52
66
|
|
|
53
67
|
uf2file = circfirm.backend.get_uf2_filepath(board, version, language)
|
|
54
68
|
uf2filename = os.path.basename(uf2file)
|
|
55
|
-
shutil.copyfile(uf2file, os.path.join(
|
|
69
|
+
shutil.copyfile(uf2file, os.path.join(bootloader, uf2filename))
|
|
56
70
|
click.echo("UF2 file copied to device!")
|
|
57
71
|
click.echo("Device should reboot momentarily.")
|
|
58
72
|
|
|
@@ -76,8 +90,6 @@ def clear(
|
|
|
76
90
|
click.echo("Cache cleared!")
|
|
77
91
|
return
|
|
78
92
|
|
|
79
|
-
board = board.replace(" ", "_").lower()
|
|
80
|
-
|
|
81
93
|
glob_pattern = "*-*" if board is None else f"*-{board}"
|
|
82
94
|
language_pattern = "-*" if language is None else f"-{language}"
|
|
83
95
|
glob_pattern += language_pattern
|
|
@@ -99,19 +111,17 @@ def clear(
|
|
|
99
111
|
@click.option("-b", "--board", default=None, help="CircuitPython board name")
|
|
100
112
|
def cache_list(board: Optional[str]) -> None:
|
|
101
113
|
"""List all the boards/versions cached."""
|
|
102
|
-
if board is not None:
|
|
103
|
-
board_name = board.replace(" ", "_").lower()
|
|
104
114
|
board_list = os.listdir(circfirm.UF2_ARCHIVE)
|
|
105
115
|
|
|
106
116
|
if not board_list:
|
|
107
117
|
click.echo("Versions have not been cached yet for any boards.")
|
|
108
118
|
sys.exit(0)
|
|
109
119
|
|
|
110
|
-
if board is not None and
|
|
111
|
-
click.echo(f"No versions for board '{
|
|
120
|
+
if board is not None and board not in board_list:
|
|
121
|
+
click.echo(f"No versions for board '{board}' are not cached.")
|
|
112
122
|
sys.exit(0)
|
|
113
123
|
|
|
114
|
-
specified_board =
|
|
124
|
+
specified_board = board if board is not None else None
|
|
115
125
|
boards = circfirm.backend.get_sorted_boards(specified_board)
|
|
116
126
|
|
|
117
127
|
for rec_boardname, rec_boardvers in boards.items():
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
circfirm/__init__.py,sha256=c5spgXArUydQ9xwHyO-32mJS61okL3IgRXYUG1JFRO4,459
|
|
2
|
+
circfirm/backend.py,sha256=75fNPs4aURHEMxh0BdWVPrRYLJaMy1IZVhebM70jDEA,5589
|
|
3
|
+
circfirm/cli.py,sha256=7UJKLKnuiR_o1FQmJ3dq-U1rcJtXuXCQvNXB2aKTjns,5148
|
|
4
|
+
circfirm/startup.py,sha256=n3OI01SoOhvV-qzGbKis6YUaOiC5eV6zZ2MHfHgG59k,1514
|
|
5
|
+
circfirm-2.0.1.dist-info/LICENSE,sha256=6pPP6gJ00tqCkxg5gABHDwWUiXZ_mBzH94xxPOqwGj4,1069
|
|
6
|
+
circfirm-2.0.1.dist-info/METADATA,sha256=mKa7MbJ6SkeE-wBno4WTgKYDwYMHb7nZLVCQ7flsL88,3775
|
|
7
|
+
circfirm-2.0.1.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
|
|
8
|
+
circfirm-2.0.1.dist-info/entry_points.txt,sha256=33qZTmSuXz8dgi29yjSyb8VsEA8HyhWuhn2MNcjatGQ,46
|
|
9
|
+
circfirm-2.0.1.dist-info/top_level.txt,sha256=qA2407wap3My6jGA5uchH2JjUM7qn73oBPwALN7GR5k,9
|
|
10
|
+
circfirm-2.0.1.dist-info/RECORD,,
|
circfirm-1.0.1.dist-info/RECORD
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
circfirm/__init__.py,sha256=c5spgXArUydQ9xwHyO-32mJS61okL3IgRXYUG1JFRO4,459
|
|
2
|
-
circfirm/backend.py,sha256=BZRAYDvu-xS7KlvRNdbTrjbwDCbb76xvc0Y5u1Z4HRE,5323
|
|
3
|
-
circfirm/cli.py,sha256=S2C8nJlY9dhY23TVDQgeJFXvPmgKFdOSRVUt3s4m-3k,4652
|
|
4
|
-
circfirm/startup.py,sha256=n3OI01SoOhvV-qzGbKis6YUaOiC5eV6zZ2MHfHgG59k,1514
|
|
5
|
-
circfirm-1.0.1.dist-info/LICENSE,sha256=6pPP6gJ00tqCkxg5gABHDwWUiXZ_mBzH94xxPOqwGj4,1069
|
|
6
|
-
circfirm-1.0.1.dist-info/METADATA,sha256=cho29ZeCk32ZpnEfeB_xwUPdQZCVhRQgvxIG1rVXIP8,3775
|
|
7
|
-
circfirm-1.0.1.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
|
|
8
|
-
circfirm-1.0.1.dist-info/entry_points.txt,sha256=33qZTmSuXz8dgi29yjSyb8VsEA8HyhWuhn2MNcjatGQ,46
|
|
9
|
-
circfirm-1.0.1.dist-info/top_level.txt,sha256=qA2407wap3My6jGA5uchH2JjUM7qn73oBPwALN7GR5k,9
|
|
10
|
-
circfirm-1.0.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|