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 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
- uf2info_file = pathlib.Path(device_path) / circfirm.UF2INFO_FILE
73
- with open(uf2info_file, encoding="utf-8") as infofile:
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
- model_line = [line.strip() for line in contents.split("\n")][1]
76
- return [comp.strip() for comp in model_line.split(":")][1]
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/{board_name}/{language}/{file}"
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
- board_name = board.replace(" ", "_").lower()
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
- board_name = board.replace(" ", "_").lower()
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
- board_name = board.replace(" ", "_").lower()
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
- filename_parts = uf2_filename.split("-")
130
- language = filename_parts[3]
131
- version_extension = "-".join(filename_parts[4:])
132
- version = version_extension[:-4]
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
- def install(version: str, language: str) -> None:
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
- mount_path = circfirm.backend.find_bootloader()
36
- if not mount_path:
37
- circuitpy = circfirm.backend.find_circuitpy()
38
- if circuitpy:
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(mount_path, uf2filename))
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 board_name not in board_list:
111
- click.echo(f"No versions for board '{board_name}' are not cached.")
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 = board_name if board is not None else None
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():
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: circfirm
3
- Version: 1.0.1
3
+ Version: 2.0.1
4
4
  Summary: CLI tool for install firmware for CircuitPython boards
5
5
  Author-email: Alec Delaney <tekktrik@gmail.com>
6
6
  License: MIT
@@ -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,,
@@ -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,,