src/eric7/MicroPython/CircuitPythonUpdater/CircupFunctions.py

branch
eric7
changeset 9740
90072e10ae9b
child 9748
df9520c864f2
equal deleted inserted replaced
9739:d527cfe919ae 9740:90072e10ae9b
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2023 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing variants of some 'circup' functions suitable for 'eric-ide'
8 integration.
9 """
10
11 #
12 # Copyright of the original sources:
13 # Copyright (c) 2019 Adafruit Industries
14 #
15
16 import os
17 import shutil
18
19 import circup
20 import requests
21
22 from PyQt6.QtCore import QCoreApplication
23
24 from eric7.EricWidgets import EricMessageBox
25
26
27 def find_modules(device_path, bundles_list):
28 """
29 Function to extract metadata from the connected device and available bundles and
30 returns this as a list of Module instances representing the modules on the device.
31
32 @param device_path path to the connected board
33 @type str
34 @param bundles_list list of supported bundles
35 @type list of circup.Bundle
36 @return list of Module instances describing the current state of the
37 modules on the connected device
38 @rtype list of circup.Module
39 """
40 result = []
41 try:
42 device_modules = circup.get_device_versions(device_path)
43 bundle_modules = circup.get_bundle_versions(bundles_list)
44 for name, device_metadata in device_modules.items():
45 if name in bundle_modules:
46 path = device_metadata["path"]
47 bundle_metadata = bundle_modules[name]
48 repo = bundle_metadata.get("__repo__")
49 bundle = bundle_metadata.get("bundle")
50 device_version = device_metadata.get("__version__")
51 bundle_version = bundle_metadata.get("__version__")
52 mpy = device_metadata["mpy"]
53 compatibility = device_metadata.get("compatibility", (None, None))
54 result.append(
55 circup.Module(
56 path,
57 repo,
58 device_version,
59 bundle_version,
60 mpy,
61 bundle,
62 compatibility,
63 )
64 )
65 except Exception as ex:
66 # If it's not possible to get the device and bundle metadata, bail out
67 # with a friendly message and indication of what's gone wrong.
68 EricMessageBox.critical(
69 None,
70 QCoreApplication.translate("CircupFunctions", "Find Modules"),
71 QCoreApplication.translate(
72 "CircupFunctions", """<p>There was an error: {0}</p>"""
73 ).format(str(ex)),
74 )
75
76 return result
77
78
79 def ensure_latest_bundle(bundle):
80 """
81 Function to ensure that there's a copy of the latest library bundle available so
82 circup can check the metadata contained therein.
83
84 @param bundle reference to the target Bundle object.
85 @type circup.Bundle
86 """
87 tag = bundle.latest_tag
88 do_update = False
89 if tag == bundle.current_tag:
90 for platform in circup.PLATFORMS:
91 # missing directories (new platform added on an existing install
92 # or side effect of pytest or network errors)
93 do_update = do_update or not os.path.isdir(bundle.lib_dir(platform))
94 else:
95 do_update = True
96
97 if do_update:
98 try:
99 circup.get_bundle(bundle, tag)
100 circup.tags_data_save_tag(bundle.key, tag)
101 except requests.exceptions.HTTPError as ex:
102 EricMessageBox.critical(
103 None,
104 QCoreApplication.translate("CircupFunctions", "Download Bundle"),
105 QCoreApplication.translate(
106 "CircupFunctions",
107 """<p>There was a problem downloading the bundle. Please try"""
108 """ again in a moment.</p><p>Error: {0}</p>""",
109 ).format(str(ex)),
110 )
111
112
113 def get_circuitpython_version(device_path):
114 """
115 Function to return the version number of CircuitPython running on the board
116 connected via ``device_path``, along with the board ID.
117
118 This is obtained from the 'boot_out.txt' file on the device, whose first line
119 will start with something like this:
120
121 Adafruit CircuitPython 4.1.0 on 2019-08-02;
122
123 While the second line is:
124
125 Board ID:raspberry_pi_pico
126
127 @param device_path path to the connected board.
128 @type str
129 @return tuple with the version string for CircuitPython and the board ID string
130 @rtype tuple of (str, str)
131 """
132 try:
133 with open(os.path.join(device_path, "boot_out.txt")) as boot:
134 version_line = boot.readline()
135 circuit_python = version_line.split(";")[0].split(" ")[-3]
136 board_line = boot.readline()
137 board_id = (
138 board_line[9:].strip()
139 if board_line.startswith("Board ID:")
140 else ""
141 )
142 except FileNotFoundError:
143 EricMessageBox.critical(
144 None,
145 QCoreApplication.translate("CircupFunctions", "Download Bundle"),
146 QCoreApplication.translate(
147 "CircupFunctions",
148 """<p>Missing file <b>boot_out.txt</b> on the device: wrong path or"""
149 """ drive corrupted.</p>""",
150 ),
151 )
152 circuit_python, board_id = "", ""
153
154 return (circuit_python, board_id)
155
156
157 def install_module(device_path, device_modules, name, py, mod_names):
158 """
159 Function to find a connected device and install a given module name.
160
161 Installation is done if it is available in the current module bundle and is not
162 already installed on the device.
163
164 @param device_path path to the connected board
165 @type str
166 @param device_modules list of module metadata from the device
167 @type list of dict
168 @param name name of the module to be installed
169 @type str
170 @param py flag indicating if the module should be installed from source or
171 from a pre-compiled module
172 @type bool
173 @param mod_names dictionary containing metadata from modules that can be generated
174 with circup.get_bundle_versions()
175 @type dict
176 @return flag indicating success
177 @rtype bool
178 """
179 if not name:
180 return False
181 elif name in mod_names:
182 library_path = os.path.join(device_path, "lib")
183 if not os.path.exists(library_path): # pragma: no cover
184 os.makedirs(library_path)
185 metadata = mod_names[name]
186 bundle = metadata["bundle"]
187 # Grab device modules to check if module already installed
188 if name in device_modules:
189 # ignore silently
190 return False
191 if py:
192 # Use Python source for module.
193 source_path = metadata["path"] # Path to Python source version.
194 if os.path.isdir(source_path):
195 target = os.path.basename(os.path.dirname(source_path))
196 target_path = os.path.join(library_path, target)
197 # Copy the directory.
198 shutil.copytree(source_path, target_path)
199 return True
200 else:
201 target = os.path.basename(source_path)
202 target_path = os.path.join(library_path, target)
203 # Copy file.
204 shutil.copyfile(source_path, target_path)
205 return True
206 else:
207 # Use pre-compiled mpy modules.
208 module_name = os.path.basename(metadata["path"]).replace(".py", ".mpy")
209 if not module_name:
210 # Must be a directory based module.
211 module_name = os.path.basename(os.path.dirname(metadata["path"]))
212 major_version = circup.CPY_VERSION.split(".")[0]
213 bundle_platform = "{0}mpy".format(major_version)
214 bundle_path = os.path.join(bundle.lib_dir(bundle_platform), module_name)
215 if os.path.isdir(bundle_path):
216 target_path = os.path.join(library_path, module_name)
217 # Copy the directory.
218 shutil.copytree(bundle_path, target_path)
219 return True
220 elif os.path.isfile(bundle_path):
221 target = os.path.basename(bundle_path)
222 target_path = os.path.join(library_path, target)
223 # Copy file.
224 shutil.copyfile(bundle_path, target_path)
225 return True
226 else:
227 EricMessageBox.critical(
228 None,
229 QCoreApplication.translate("CircupFunctions", "Install Modules"),
230 QCoreApplication.translate(
231 "CircupFunctions",
232 """<p>The compiled version of module <b>{0}</b> cannot be"""
233 """ found.</p>""",
234 ).format(name),
235 )
236 return False
237 else:
238 EricMessageBox.critical(
239 None,
240 QCoreApplication.translate("CircupFunctions", "Install Modules"),
241 QCoreApplication.translate(
242 "CircupFunctions", """<p>The module name <b>{0}</b> is not known.</p>"""
243 ).format(name),
244 )
245 return False
246
247
248 def patch_circup():
249 """
250 Function to patch 'circup' to use our functions adapted to the use within the
251 eric-ide.
252 """
253 circup.ensure_latest_bundle = ensure_latest_bundle
254 circup.find_modules = find_modules
255 circup.get_circuitpython_version = get_circuitpython_version
256 circup.install_module = install_module

eric ide

mercurial