circfirm 3.1.0__tar.gz → 4.0.0__tar.gz

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.
Files changed (120) hide show
  1. {circfirm-3.1.0 → circfirm-4.0.0}/.github/workflows/push.yml +5 -5
  2. {circfirm-3.1.0 → circfirm-4.0.0}/.pre-commit-config.yaml +1 -1
  3. {circfirm-3.1.0 → circfirm-4.0.0}/Makefile +2 -2
  4. {circfirm-3.1.0 → circfirm-4.0.0}/PKG-INFO +16 -16
  5. circfirm-4.0.0/VERSION +1 -0
  6. {circfirm-3.1.0 → circfirm-4.0.0}/circfirm/backend/__init__.py +1 -2
  7. {circfirm-3.1.0 → circfirm-4.0.0}/circfirm/backend/cache.py +8 -7
  8. {circfirm-3.1.0 → circfirm-4.0.0}/circfirm/backend/device.py +2 -2
  9. {circfirm-3.1.0 → circfirm-4.0.0}/circfirm/backend/github.py +4 -4
  10. {circfirm-3.1.0 → circfirm-4.0.0}/circfirm/backend/s3.py +2 -2
  11. {circfirm-3.1.0 → circfirm-4.0.0}/circfirm/cli/__init__.py +21 -7
  12. {circfirm-3.1.0 → circfirm-4.0.0}/circfirm/cli/current.py +1 -3
  13. circfirm-4.0.0/circfirm/cli/detect.py +38 -0
  14. {circfirm-3.1.0 → circfirm-4.0.0}/circfirm/cli/install.py +13 -2
  15. {circfirm-3.1.0 → circfirm-4.0.0}/circfirm/cli/update.py +14 -2
  16. {circfirm-3.1.0 → circfirm-4.0.0}/circfirm/startup.py +3 -4
  17. {circfirm-3.1.0 → circfirm-4.0.0}/circfirm.egg-info/PKG-INFO +16 -16
  18. {circfirm-3.1.0 → circfirm-4.0.0}/circfirm.egg-info/SOURCES.txt +3 -0
  19. circfirm-4.0.0/circfirm.egg-info/requires.txt +17 -0
  20. circfirm-4.0.0/docs/commands/detect.rst +30 -0
  21. {circfirm-3.1.0 → circfirm-4.0.0}/docs/commands/install.rst +8 -0
  22. {circfirm-3.1.0 → circfirm-4.0.0}/docs/commands/update.rst +8 -0
  23. {circfirm-3.1.0 → circfirm-4.0.0}/docs/conf.py +0 -1
  24. {circfirm-3.1.0 → circfirm-4.0.0}/docs/index.rst +1 -0
  25. {circfirm-3.1.0 → circfirm-4.0.0}/pyproject.toml +4 -3
  26. {circfirm-3.1.0 → circfirm-4.0.0}/requirements-dev.txt +6 -6
  27. {circfirm-3.1.0 → circfirm-4.0.0}/requirements.txt +6 -6
  28. {circfirm-3.1.0 → circfirm-4.0.0}/scripts/rmdir.py +8 -0
  29. {circfirm-3.1.0 → circfirm-4.0.0}/tests/backend/test_backend_s3.py +2 -3
  30. {circfirm-3.1.0 → circfirm-4.0.0}/tests/cli/test_cli_cache.py +1 -0
  31. circfirm-4.0.0/tests/cli/test_cli_detect.py +56 -0
  32. {circfirm-3.1.0 → circfirm-4.0.0}/tests/cli/test_cli_install.py +35 -0
  33. {circfirm-3.1.0 → circfirm-4.0.0}/tests/cli/test_cli_update.py +15 -0
  34. {circfirm-3.1.0 → circfirm-4.0.0}/tests/helpers.py +4 -4
  35. circfirm-3.1.0/VERSION +0 -1
  36. circfirm-3.1.0/circfirm.egg-info/requires.txt +0 -17
  37. {circfirm-3.1.0 → circfirm-4.0.0}/.github/workflows/codeql.yml +0 -0
  38. {circfirm-3.1.0 → circfirm-4.0.0}/.github/workflows/publish.yml +0 -0
  39. {circfirm-3.1.0 → circfirm-4.0.0}/.gitignore +0 -0
  40. {circfirm-3.1.0 → circfirm-4.0.0}/.readthedocs.yaml +0 -0
  41. {circfirm-3.1.0 → circfirm-4.0.0}/.reuse/dep5 +0 -0
  42. {circfirm-3.1.0 → circfirm-4.0.0}/LICENSE +0 -0
  43. {circfirm-3.1.0 → circfirm-4.0.0}/LICENSES/MIT.txt +0 -0
  44. {circfirm-3.1.0 → circfirm-4.0.0}/LICENSES/Unlicense.txt +0 -0
  45. {circfirm-3.1.0 → circfirm-4.0.0}/NOTICE +0 -0
  46. {circfirm-3.1.0 → circfirm-4.0.0}/NOTICE.license +0 -0
  47. {circfirm-3.1.0 → circfirm-4.0.0}/README.rst +0 -0
  48. {circfirm-3.1.0 → circfirm-4.0.0}/VERSION.license +0 -0
  49. {circfirm-3.1.0 → circfirm-4.0.0}/circfirm/__init__.py +0 -0
  50. {circfirm-3.1.0 → circfirm-4.0.0}/circfirm/cli/about.py +0 -0
  51. {circfirm-3.1.0 → circfirm-4.0.0}/circfirm/cli/cache.py +0 -0
  52. {circfirm-3.1.0 → circfirm-4.0.0}/circfirm/cli/config.py +0 -0
  53. {circfirm-3.1.0 → circfirm-4.0.0}/circfirm/cli/query.py +0 -0
  54. {circfirm-3.1.0 → circfirm-4.0.0}/circfirm/py.typed +0 -0
  55. {circfirm-3.1.0 → circfirm-4.0.0}/circfirm/templates/settings.yaml +0 -0
  56. {circfirm-3.1.0 → circfirm-4.0.0}/circfirm/templates/settings.yaml.license +0 -0
  57. {circfirm-3.1.0 → circfirm-4.0.0}/circfirm.egg-info/dependency_links.txt +0 -0
  58. {circfirm-3.1.0 → circfirm-4.0.0}/circfirm.egg-info/entry_points.txt +0 -0
  59. {circfirm-3.1.0 → circfirm-4.0.0}/circfirm.egg-info/top_level.txt +0 -0
  60. {circfirm-3.1.0 → circfirm-4.0.0}/docs/commands/cache.rst +0 -0
  61. {circfirm-3.1.0 → circfirm-4.0.0}/docs/commands/config.rst +0 -0
  62. {circfirm-3.1.0 → circfirm-4.0.0}/docs/commands/current.rst +0 -0
  63. {circfirm-3.1.0 → circfirm-4.0.0}/docs/commands/query.rst +0 -0
  64. {circfirm-3.1.0 → circfirm-4.0.0}/docs/examples/update_many.rst +0 -0
  65. {circfirm-3.1.0 → circfirm-4.0.0}/docs/examples/weekly_cache.rst +0 -0
  66. {circfirm-3.1.0 → circfirm-4.0.0}/docs/index.rst.license +0 -0
  67. {circfirm-3.1.0 → circfirm-4.0.0}/docs/infrastructure/ci.rst +0 -0
  68. {circfirm-3.1.0 → circfirm-4.0.0}/docs/infrastructure/codecheck.rst +0 -0
  69. {circfirm-3.1.0 → circfirm-4.0.0}/docs/infrastructure/docs.rst +0 -0
  70. {circfirm-3.1.0 → circfirm-4.0.0}/docs/infrastructure/license.rst +0 -0
  71. {circfirm-3.1.0 → circfirm-4.0.0}/docs/infrastructure/notice.rst +0 -0
  72. {circfirm-3.1.0 → circfirm-4.0.0}/docs/infrastructure/tests.rst +0 -0
  73. {circfirm-3.1.0 → circfirm-4.0.0}/examples/bash/update_many.sh +0 -0
  74. {circfirm-3.1.0 → circfirm-4.0.0}/examples/bash/weekly_cache.sh +0 -0
  75. {circfirm-3.1.0 → circfirm-4.0.0}/examples/powershell/update_many.ps1 +0 -0
  76. {circfirm-3.1.0 → circfirm-4.0.0}/examples/powershell/weekly_cache.ps1 +0 -0
  77. {circfirm-3.1.0 → circfirm-4.0.0}/setup.cfg +0 -0
  78. {circfirm-3.1.0 → circfirm-4.0.0}/tests/assets/boot_out.txt +0 -0
  79. {circfirm-3.1.0 → circfirm-4.0.0}/tests/assets/firmwares/feather_m0_express/adafruit-circuitpython-feather_m0_express-en_US-7.0.0.uf2 +0 -0
  80. {circfirm-3.1.0 → circfirm-4.0.0}/tests/assets/firmwares/feather_m0_express/adafruit-circuitpython-feather_m0_express-en_US-7.1.0.uf2 +0 -0
  81. {circfirm-3.1.0 → circfirm-4.0.0}/tests/assets/firmwares/feather_m0_express/adafruit-circuitpython-feather_m0_express-en_US-7.2.0.uf2 +0 -0
  82. {circfirm-3.1.0 → circfirm-4.0.0}/tests/assets/firmwares/feather_m0_express/adafruit-circuitpython-feather_m0_express-fr-7.0.0.uf2 +0 -0
  83. {circfirm-3.1.0 → circfirm-4.0.0}/tests/assets/firmwares/feather_m0_express/adafruit-circuitpython-feather_m0_express-fr-7.1.0.uf2 +0 -0
  84. {circfirm-3.1.0 → circfirm-4.0.0}/tests/assets/firmwares/feather_m0_express/adafruit-circuitpython-feather_m0_express-fr-7.2.0.uf2 +0 -0
  85. {circfirm-3.1.0 → circfirm-4.0.0}/tests/assets/firmwares/feather_m0_express/adafruit-circuitpython-feather_m0_express-zh_Latn_pinyin-7.0.0.uf2 +0 -0
  86. {circfirm-3.1.0 → circfirm-4.0.0}/tests/assets/firmwares/feather_m0_express/adafruit-circuitpython-feather_m0_express-zh_Latn_pinyin-7.1.0.uf2 +0 -0
  87. {circfirm-3.1.0 → circfirm-4.0.0}/tests/assets/firmwares/feather_m0_express/adafruit-circuitpython-feather_m0_express-zh_Latn_pinyin-7.2.0.uf2 +0 -0
  88. {circfirm-3.1.0 → circfirm-4.0.0}/tests/assets/firmwares/feather_m4_express/adafruit-circuitpython-feather_m4_express-en_US-7.0.0.uf2 +0 -0
  89. {circfirm-3.1.0 → circfirm-4.0.0}/tests/assets/firmwares/feather_m4_express/adafruit-circuitpython-feather_m4_express-en_US-7.1.0.uf2 +0 -0
  90. {circfirm-3.1.0 → circfirm-4.0.0}/tests/assets/firmwares/feather_m4_express/adafruit-circuitpython-feather_m4_express-en_US-7.2.0.uf2 +0 -0
  91. {circfirm-3.1.0 → circfirm-4.0.0}/tests/assets/firmwares/feather_m4_express/adafruit-circuitpython-feather_m4_express-fr-7.0.0.uf2 +0 -0
  92. {circfirm-3.1.0 → circfirm-4.0.0}/tests/assets/firmwares/feather_m4_express/adafruit-circuitpython-feather_m4_express-fr-7.1.0.uf2 +0 -0
  93. {circfirm-3.1.0 → circfirm-4.0.0}/tests/assets/firmwares/feather_m4_express/adafruit-circuitpython-feather_m4_express-fr-7.2.0.uf2 +0 -0
  94. {circfirm-3.1.0 → circfirm-4.0.0}/tests/assets/firmwares/feather_m4_express/adafruit-circuitpython-feather_m4_express-zh_Latn_pinyin-7.0.0.uf2 +0 -0
  95. {circfirm-3.1.0 → circfirm-4.0.0}/tests/assets/firmwares/feather_m4_express/adafruit-circuitpython-feather_m4_express-zh_Latn_pinyin-7.1.0.uf2 +0 -0
  96. {circfirm-3.1.0 → circfirm-4.0.0}/tests/assets/firmwares/feather_m4_express/adafruit-circuitpython-feather_m4_express-zh_Latn_pinyin-7.2.0.uf2 +0 -0
  97. {circfirm-3.1.0 → circfirm-4.0.0}/tests/assets/firmwares/pygamer/adafruit-circuitpython-pygamer-en_US-7.0.0.uf2 +0 -0
  98. {circfirm-3.1.0 → circfirm-4.0.0}/tests/assets/firmwares/pygamer/adafruit-circuitpython-pygamer-en_US-7.1.0.uf2 +0 -0
  99. {circfirm-3.1.0 → circfirm-4.0.0}/tests/assets/firmwares/pygamer/adafruit-circuitpython-pygamer-en_US-7.2.0.uf2 +0 -0
  100. {circfirm-3.1.0 → circfirm-4.0.0}/tests/assets/firmwares/pygamer/adafruit-circuitpython-pygamer-fr-7.0.0.uf2 +0 -0
  101. {circfirm-3.1.0 → circfirm-4.0.0}/tests/assets/firmwares/pygamer/adafruit-circuitpython-pygamer-fr-7.1.0.uf2 +0 -0
  102. {circfirm-3.1.0 → circfirm-4.0.0}/tests/assets/firmwares/pygamer/adafruit-circuitpython-pygamer-fr-7.2.0.uf2 +0 -0
  103. {circfirm-3.1.0 → circfirm-4.0.0}/tests/assets/firmwares/pygamer/adafruit-circuitpython-pygamer-zh_Latn_pinyin-7.0.0.uf2 +0 -0
  104. {circfirm-3.1.0 → circfirm-4.0.0}/tests/assets/firmwares/pygamer/adafruit-circuitpython-pygamer-zh_Latn_pinyin-7.1.0.uf2 +0 -0
  105. {circfirm-3.1.0 → circfirm-4.0.0}/tests/assets/firmwares/pygamer/adafruit-circuitpython-pygamer-zh_Latn_pinyin-7.2.0.uf2 +0 -0
  106. {circfirm-3.1.0 → circfirm-4.0.0}/tests/assets/info_uf2.txt +0 -0
  107. {circfirm-3.1.0 → circfirm-4.0.0}/tests/assets/responses/full_list.txt +0 -0
  108. {circfirm-3.1.0 → circfirm-4.0.0}/tests/assets/responses/full_list.txt.license +0 -0
  109. {circfirm-3.1.0 → circfirm-4.0.0}/tests/assets/responses/specific_board.txt +0 -0
  110. {circfirm-3.1.0 → circfirm-4.0.0}/tests/assets/responses/specific_board.txt.license +0 -0
  111. {circfirm-3.1.0 → circfirm-4.0.0}/tests/backend/test_backend.py +0 -0
  112. {circfirm-3.1.0 → circfirm-4.0.0}/tests/backend/test_backend_cache.py +0 -0
  113. {circfirm-3.1.0 → circfirm-4.0.0}/tests/backend/test_backend_device.py +0 -0
  114. {circfirm-3.1.0 → circfirm-4.0.0}/tests/backend/test_backend_github.py +0 -0
  115. {circfirm-3.1.0 → circfirm-4.0.0}/tests/cli/test_cli_about.py +0 -0
  116. {circfirm-3.1.0 → circfirm-4.0.0}/tests/cli/test_cli_config.py +0 -0
  117. {circfirm-3.1.0 → circfirm-4.0.0}/tests/cli/test_cli_current.py +0 -0
  118. {circfirm-3.1.0 → circfirm-4.0.0}/tests/cli/test_cli_query.py +0 -0
  119. {circfirm-3.1.0 → circfirm-4.0.0}/tests/conftest.py +0 -0
  120. {circfirm-3.1.0 → circfirm-4.0.0}/tests/test_startup.py +0 -0
@@ -16,11 +16,11 @@ jobs:
16
16
  fail-fast: false
17
17
  matrix:
18
18
  py-version: [
19
- "3.8",
20
19
  "3.9",
21
20
  "3.10",
22
21
  "3.11",
23
22
  "3.12",
23
+ "3.13",
24
24
  ]
25
25
  os: [
26
26
  "ubuntu-latest",
@@ -28,22 +28,22 @@ jobs:
28
28
  "macos-latest",
29
29
  ]
30
30
  exclude:
31
- - os: windows-latest
32
- py-version: "3.9"
33
31
  - os: windows-latest
34
32
  py-version: "3.10"
35
33
  - os: windows-latest
36
34
  py-version: "3.11"
37
35
  - os: windows-latest
38
36
  py-version: "3.12"
39
- - os: macos-latest
40
- py-version: "3.9"
37
+ - os: windows-latest
38
+ py-version: "3.13"
41
39
  - os: macos-latest
42
40
  py-version: "3.10"
43
41
  - os: macos-latest
44
42
  py-version: "3.11"
45
43
  - os: macos-latest
46
44
  py-version: "3.12"
45
+ - os: macos-latest
46
+ py-version: "3.13"
47
47
 
48
48
  steps:
49
49
  - name: Setup Python 3.x
@@ -14,7 +14,7 @@ repos:
14
14
  - id: reuse
15
15
  name: Check REUSE compatibility
16
16
  - repo: https://github.com/pre-commit/pre-commit-hooks
17
- rev: v4.5.0
17
+ rev: v5.0.0
18
18
  hooks:
19
19
  - id: check-yaml
20
20
  name: Check YAML
@@ -59,8 +59,8 @@ test-run:
59
59
  test-clean:
60
60
  ifeq "$(OS)" "Windows_NT"
61
61
  -@subst T: /d
62
- -@python scripts/rmdir.py testmount
63
- -@python scripts/rmdir.py tests/sandbox/circuitpython
62
+ -@python scripts\rmdir.py testmount
63
+ -@python scripts\rmdir.py tests\sandbox\circuitpython
64
64
  else ifeq "$(shell uname -s)" "Linux"
65
65
  -@sudo umount testmount
66
66
  -@sudo rm -rf testmount
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.2
2
2
  Name: circfirm
3
- Version: 3.1.0
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,37 +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.8.0
28
+ Requires-Python: >=3.9.0
29
29
  Description-Content-Type: text/x-rst
30
30
  License-File: LICENSE
31
31
  License-File: NOTICE
32
32
  License-File: NOTICE.license
33
- Requires-Dist: boto3~=1.34
34
- Requires-Dist: click~=8.0
33
+ Requires-Dist: boto3~=1.35
34
+ Requires-Dist: click~=8.1
35
35
  Requires-Dist: click-spinner~=0.1
36
- Requires-Dist: packaging~=23.2
37
- Requires-Dist: psutil~=5.9
36
+ Requires-Dist: packaging~=24.2
37
+ Requires-Dist: psutil~=6.1
38
38
  Requires-Dist: pyyaml~=6.0
39
- Requires-Dist: requests~=2.31
40
- Requires-Dist: boto3-stubs[essential]~=1.34
39
+ Requires-Dist: requests~=2.32
40
+ Requires-Dist: boto3-stubs[essential]~=1.35
41
41
  Provides-Extra: dev
42
- Requires-Dist: build~=1.0; extra == "dev"
43
- Requires-Dist: coverage~=7.4; extra == "dev"
44
- Requires-Dist: pre-commit~=2.20; extra == "dev"
45
- Requires-Dist: pytest~=8.0; extra == "dev"
46
- Requires-Dist: sphinx~=5.1; extra == "dev"
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
47
  Requires-Dist: sphinx-tabs~=3.4; extra == "dev"
48
- Requires-Dist: sphinx-rtd-theme~=1.0; extra == "dev"
48
+ Requires-Dist: sphinx-rtd-theme~=3.0; extra == "dev"
49
49
 
50
50
  ..
51
51
  SPDX-FileCopyrightText: 2022 Alec Delaney, for Adafruit Industries
circfirm-4.0.0/VERSION ADDED
@@ -0,0 +1 @@
1
+ 4.0.0
@@ -9,7 +9,6 @@ Author(s): Alec Delaney
9
9
 
10
10
  import enum
11
11
  import re
12
- from typing import Tuple
13
12
 
14
13
 
15
14
  class Language(enum.Enum):
@@ -60,7 +59,7 @@ def get_uf2_filename(board_id: str, version: str, language: str = "en_US") -> st
60
59
  return f"adafruit-circuitpython-{board_id}-{language}-{version}.uf2"
61
60
 
62
61
 
63
- def parse_firmware_info(uf2_filename: str) -> Tuple[str, str]:
62
+ def parse_firmware_info(uf2_filename: str) -> tuple[str, str]:
64
63
  """Get firmware info."""
65
64
  regex_match = re.match(FIRMWARE_REGEX, uf2_filename)
66
65
  if regex_match is None:
@@ -9,8 +9,7 @@ Author(s): Alec Delaney
9
9
 
10
10
  import os
11
11
  import pathlib
12
- import re
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
@@ -48,19 +47,21 @@ def download_uf2(board_id: str, version: str, language: str = "en_US") -> None:
48
47
 
49
48
  SUCCESS = 200
50
49
  if response.status_code != SUCCESS:
51
- raise ConnectionError(f"Could not download the specified UF2 file:\n{url}")
50
+ raise ConnectionError(
51
+ f"Could not download the specified UF2 file:\n{url}\nAre the board ID, version, and language correct?"
52
+ )
52
53
 
53
54
  uf2_file.parent.mkdir(parents=True, exist_ok=True)
54
55
  with open(uf2_file, mode="wb") as uf2file:
55
56
  uf2file.write(response.content)
56
57
 
57
58
 
58
- def get_sorted_boards(board_id: Optional[str]) -> Dict[str, Dict[str, Set[str]]]:
59
+ def get_sorted_boards(board_id: Optional[str]) -> dict[str, dict[str, set[str]]]:
59
60
  """Get a sorted collection of boards, versions, and languages."""
60
- boards: Dict[str, Dict[str, Set[str]]] = {}
61
+ boards: dict[str, dict[str, set[str]]] = {}
61
62
  for board_folder in sorted(os.listdir(circfirm.UF2_ARCHIVE)):
62
- versions: Dict[str, List[str]] = {}
63
- sorted_versions: Dict[str, Set[str]] = {}
63
+ versions: dict[str, list[str]] = {}
64
+ sorted_versions: dict[str, set[str]] = {}
64
65
  if board_id is not None and board_id != board_folder:
65
66
  continue
66
67
  board_folder_full = get_board_folder(board_folder)
@@ -9,7 +9,7 @@ Author(s): Alec Delaney
9
9
 
10
10
  import pathlib
11
11
  import re
12
- from typing import Optional, Tuple
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) -> Tuple[str, 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:
@@ -9,7 +9,7 @@ Author(s): Alec Delaney
9
9
 
10
10
  import datetime
11
11
  import re
12
- from typing import List, Tuple, TypedDict
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() -> Tuple[int, int, datetime.datetime]:
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) -> List[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: List[GitTreeItem] = response.json()["tree"]
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:
@@ -8,7 +8,7 @@ Author(s): Alec Delaney
8
8
  """
9
9
 
10
10
  import re
11
- from typing import List, Optional
11
+ from typing import Optional
12
12
 
13
13
  import boto3
14
14
  import botocore
@@ -27,7 +27,7 @@ BUCKET = S3_RESOURCE.Bucket(BUCKET_NAME)
27
27
 
28
28
  def get_board_versions(
29
29
  board_id: str, language: str = "en_US", *, regex: Optional[str] = None
30
- ) -> List[str]:
30
+ ) -> list[str]:
31
31
  """Get a list of CircuitPython versions for a given board."""
32
32
  prefix = f"bin/{board_id}/{language}"
33
33
  firmware_regex = circfirm.backend.FIRMWARE_REGEX_PATTERN.replace(
@@ -13,7 +13,8 @@ import pkgutil
13
13
  import shutil
14
14
  import sys
15
15
  import time
16
- from typing import Any, Callable, Dict, Iterable, Optional, Tuple, TypeVar
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], bootloader: Optional[str], board: Optional[str]
47
- ) -> Tuple[str, str]:
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.sleep(1)
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() -> Tuple[Optional[str], Optional[str]]:
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()
@@ -116,7 +130,7 @@ def announce_and_await(
116
130
  msg: str,
117
131
  func: Callable[..., _T],
118
132
  args: Iterable = (),
119
- kwargs: Optional[Dict[str, Any]] = None,
133
+ kwargs: Optional[dict[str, Any]] = None,
120
134
  *,
121
135
  use_spinner: bool = True,
122
136
  ) -> _T:
@@ -141,7 +155,7 @@ def announce_and_await(
141
155
  raise err
142
156
 
143
157
 
144
- def get_settings() -> Dict[str, Any]:
158
+ def get_settings() -> dict[str, Any]:
145
159
  """Get the contents of the settings file."""
146
160
  with open(circfirm.SETTINGS_FILE, encoding="utf-8") as yamlfile:
147
161
  return yaml.safe_load(yamlfile)
@@ -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() -> Tuple[str, str]:
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:
@@ -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)
@@ -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
- def cli(version: str, language: str, board_id: Optional[str]) -> None:
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
- bootloader, board_id = circfirm.cli.get_board_id(circuitpy, bootloader, board_id)
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)
@@ -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",
@@ -46,9 +52,10 @@ import circfirm.cli.install
46
52
  default=False,
47
53
  help="Upgrade up to patch version updates",
48
54
  )
49
- def cli(
55
+ def cli( # noqa: PLR0913
50
56
  board_id: Optional[str],
51
57
  language: str,
58
+ timeout: int,
52
59
  pre_release: bool,
53
60
  limit_to_minor: bool,
54
61
  limit_to_patch: bool,
@@ -65,7 +72,12 @@ def cli(
65
72
  "The latest version will be installed regardless of the currently installed version."
66
73
  )
67
74
  current_version = "0.0.0"
68
- bootloader, board_id = circfirm.cli.get_board_id(circuitpy, bootloader, board_id)
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])
69
81
 
70
82
  new_versions = circfirm.backend.s3.get_board_versions(board_id, language)
71
83
 
@@ -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: List[str] = []
18
- FILE_LIST: List[str] = []
19
- TEMPLATE_LIST: List[Tuple[str, str]] = []
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
1
+ Metadata-Version: 2.2
2
2
  Name: circfirm
3
- Version: 3.1.0
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,37 +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.8.0
28
+ Requires-Python: >=3.9.0
29
29
  Description-Content-Type: text/x-rst
30
30
  License-File: LICENSE
31
31
  License-File: NOTICE
32
32
  License-File: NOTICE.license
33
- Requires-Dist: boto3~=1.34
34
- Requires-Dist: click~=8.0
33
+ Requires-Dist: boto3~=1.35
34
+ Requires-Dist: click~=8.1
35
35
  Requires-Dist: click-spinner~=0.1
36
- Requires-Dist: packaging~=23.2
37
- Requires-Dist: psutil~=5.9
36
+ Requires-Dist: packaging~=24.2
37
+ Requires-Dist: psutil~=6.1
38
38
  Requires-Dist: pyyaml~=6.0
39
- Requires-Dist: requests~=2.31
40
- Requires-Dist: boto3-stubs[essential]~=1.34
39
+ Requires-Dist: requests~=2.32
40
+ Requires-Dist: boto3-stubs[essential]~=1.35
41
41
  Provides-Extra: dev
42
- Requires-Dist: build~=1.0; extra == "dev"
43
- Requires-Dist: coverage~=7.4; extra == "dev"
44
- Requires-Dist: pre-commit~=2.20; extra == "dev"
45
- Requires-Dist: pytest~=8.0; extra == "dev"
46
- Requires-Dist: sphinx~=5.1; extra == "dev"
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
47
  Requires-Dist: sphinx-tabs~=3.4; extra == "dev"
48
- Requires-Dist: sphinx-rtd-theme~=1.0; extra == "dev"
48
+ Requires-Dist: sphinx-rtd-theme~=3.0; extra == "dev"
49
49
 
50
50
  ..
51
51
  SPDX-FileCopyrightText: 2022 Alec Delaney, for Adafruit Industries
@@ -36,6 +36,7 @@ circfirm/cli/about.py
36
36
  circfirm/cli/cache.py
37
37
  circfirm/cli/config.py
38
38
  circfirm/cli/current.py
39
+ circfirm/cli/detect.py
39
40
  circfirm/cli/install.py
40
41
  circfirm/cli/query.py
41
42
  circfirm/cli/update.py
@@ -47,6 +48,7 @@ docs/index.rst.license
47
48
  docs/commands/cache.rst
48
49
  docs/commands/config.rst
49
50
  docs/commands/current.rst
51
+ docs/commands/detect.rst
50
52
  docs/commands/install.rst
51
53
  docs/commands/query.rst
52
54
  docs/commands/update.rst
@@ -108,6 +110,7 @@ tests/cli/test_cli_about.py
108
110
  tests/cli/test_cli_cache.py
109
111
  tests/cli/test_cli_config.py
110
112
  tests/cli/test_cli_current.py
113
+ tests/cli/test_cli_detect.py
111
114
  tests/cli/test_cli_install.py
112
115
  tests/cli/test_cli_query.py
113
116
  tests/cli/test_cli_update.py
@@ -0,0 +1,17 @@
1
+ boto3~=1.35
2
+ click~=8.1
3
+ click-spinner~=0.1
4
+ packaging~=24.2
5
+ psutil~=6.1
6
+ pyyaml~=6.0
7
+ requests~=2.32
8
+ boto3-stubs[essential]~=1.35
9
+
10
+ [dev]
11
+ build~=1.2
12
+ coverage~=7.6
13
+ pre-commit~=4.0
14
+ pytest~=8.3
15
+ sphinx~=7.4
16
+ sphinx-tabs~=3.4
17
+ sphinx-rtd-theme~=3.0
@@ -0,0 +1,30 @@
1
+ ..
2
+ SPDX-FileCopyrightText: 2024 Alec Delaney, for Adafruit Industries
3
+ SPDX-License-Identifier: MIT
4
+
5
+ Detecting Connected Boards
6
+ ==========================
7
+
8
+ You can detect connected CircuitPython boards using ``circfirm detect``.
9
+
10
+ See ``circfirm detect --help`` and ``circfirm detect [command] --help`` for more information on commands.
11
+
12
+ Detecting a CIRCUITPY Board
13
+ ---------------------------
14
+
15
+ You can detect a connected CircuitPython board in CIRCUITPY or equivalent mode using ``circfirm detect circuitpy``.
16
+
17
+ .. code-block:: shell
18
+
19
+ # Detect a connected board in CIRCUITPY (or equivalent) mode
20
+ circfirm detect circuitpy
21
+
22
+ Detecting a Bootloader Board
23
+ ----------------------------
24
+
25
+ You can detect a connected CircuitPython board in bootloader mode using ``circfirm detect bootloader``.
26
+
27
+ .. code-block:: shell
28
+
29
+ # Detect a connected board in bootloader mode
30
+ circfirm detect bootloader
@@ -19,6 +19,10 @@ bootloader mode, you can do so and simply use the ``--board-id`` option to provi
19
19
 
20
20
  You can specify a language using the ``--language`` option - the default is US English.
21
21
 
22
+ If you would like to specify a timeout for how long the CLI will wait for a device in bootloader
23
+ mode in secounds (e.g., for scripting), you can use the ``--timeout`` option. The default behavior
24
+ is that it will wait indefinitely (``-1`` secounds).
25
+
22
26
  .. code-block:: shell
23
27
 
24
28
  # Install CircuitPython 8.0.0 on the connected board
@@ -29,3 +33,7 @@ You can specify a language using the ``--language`` option - the default is US E
29
33
 
30
34
  # Install CircuitPython 8.0.0 on the connected Adafruit QT Py ESP32 Pico (in bootloader mode)
31
35
  circfirm install 8.0.0 --board-id adafruit_qtpy_esp32_pico
36
+
37
+ # Install CircuitPython 8.0.0 but only wait up to 30 seconds for the device to change from
38
+ # bootloader mode
39
+ circfirm install 8.0.0 --timeout 30
@@ -19,6 +19,10 @@ bootloader mode, you can do so and simply use the ``--board-id`` option to provi
19
19
 
20
20
  You can specify a language using the ``--language`` option - the default is US English.
21
21
 
22
+ If you would like to specify a timeout for how long the CLI will wait for a device in bootloader
23
+ mode in secounds (e.g., for scripting), you can use the ``--timeout`` option. The default behavior
24
+ is that it will wait indefinitely (``-1`` secounds).
25
+
22
26
  If you would like to include pre-releases as potential update versions, you can use the
23
27
  ``--pre-release`` flag.
24
28
 
@@ -44,3 +48,7 @@ both are used, the more limiting flag (``--limit-to-patch``) will take precedenc
44
48
 
45
49
  # Update CircuitPython on the connected board, considering pre-release versions
46
50
  circfirm update --pre-release
51
+
52
+ # Update CircuitPython but only wait up to 30 seconds for the device to change from
53
+ # bootloader mode
54
+ circfirm install 8.0.0 --timeout 30
@@ -38,4 +38,3 @@ intersphinx_mapping = {
38
38
  # https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
39
39
 
40
40
  html_theme = "sphinx_rtd_theme"
41
- html_theme_path = [sphinx_rtd_theme.get_html_theme_path(), "."]
@@ -19,6 +19,7 @@
19
19
 
20
20
  commands/update
21
21
  commands/install
22
+ commands/detect
22
23
  commands/current
23
24
  commands/cache
24
25
  commands/query