modm_data.dl.stmicro
1# Copyright 2022, Niklas Hauser 2# SPDX-License-Identifier: MPL-2.0 3 4from .document import Document, load_remote_info, load_local_info, store_remote_info, store_local_info 5from .cubemx import download_cubemx 6from .cubeprog import download_cubeprog 7 8__all__ = [ 9 "Document", 10 "load_remote_info", 11 "load_local_info", 12 "store_remote_info", 13 "store_local_info", 14 "download_cubemx", 15 "download_cubeprog", 16]
class
Document:
14class Document: 15 """ 16 Smaller helper class to store all relevant information about a remote 17 STMicro PDF document and how to download it. 18 """ 19 20 def __init__(self, data): 21 # Primary information from JSON data 22 self._title = data["title"] 23 self._description = data["localizedDescriptions"]["en"] 24 self._url = data["localizedLinks"]["en"] 25 self._version = data["version"] 26 self._update = data["latestUpdate"] 27 self._type = data.get("resourceType", "Unknown") 28 self._md_url = data.get("localizedMdZipLinks", {}).get("en") 29 30 # Derived information 31 self._short_type = self._title[:2] 32 clean_version = self._version.replace(".0", "").replace(".", "_") 33 self.name = self._title + "-v" + clean_version 34 """The full name of the document including version.""" 35 self.filename = self.name + ".pdf" 36 """The leaf filename of the document.""" 37 self.url = "https://www.st.com" + self._url 38 """The full URL that the file was downloaded from.""" 39 self.md_url = "https://www.st.com" + self._md_url if self._md_url else None 40 """The full URL of the zipped Markdown version or `None` if there is none.""" 41 42 @property 43 def data(self) -> dict: 44 """ 45 A dictionary uniquely identifying this document version in a similar 46 format to the one used by the STMicro homepage. This can be used to 47 keep track of which documents have already been downloaded and which 48 have been updated upstream. 49 """ 50 return { 51 "title": self._title, 52 "localizedDescriptions": {"en": self._description}, 53 "localizedLinks": {"en": self._url}, 54 "version": self._version, 55 "latestUpdate": self._update, 56 "resourceType": self._type, 57 } 58 59 def store_pdf(self, path: str, overwrite: bool = False) -> bool: 60 """Download the PDF file to the path, optionally overwriting it.""" 61 return download_file(self.url, path, overwrite=overwrite) 62 63 def store_markdown(self, path: str, overwrite: bool = False) -> bool: 64 """Download the zipped Markdown version to the path, optionally overwriting it.""" 65 return download_file(self.md_url, path, overwrite=overwrite) 66 67 def __repr__(self) -> str: 68 return f"Doc({self._title} v{self._version.replace('.0', '')})" 69 70 def __eq__(self, other) -> bool: 71 if not isinstance(other, type(self)): 72 return False 73 return self.filename == other.filename 74 75 def __hash__(self) -> int: 76 return hash(self.filename)
Smaller helper class to store all relevant information about a remote STMicro PDF document and how to download it.
Document(data)
20 def __init__(self, data): 21 # Primary information from JSON data 22 self._title = data["title"] 23 self._description = data["localizedDescriptions"]["en"] 24 self._url = data["localizedLinks"]["en"] 25 self._version = data["version"] 26 self._update = data["latestUpdate"] 27 self._type = data.get("resourceType", "Unknown") 28 self._md_url = data.get("localizedMdZipLinks", {}).get("en") 29 30 # Derived information 31 self._short_type = self._title[:2] 32 clean_version = self._version.replace(".0", "").replace(".", "_") 33 self.name = self._title + "-v" + clean_version 34 """The full name of the document including version.""" 35 self.filename = self.name + ".pdf" 36 """The leaf filename of the document.""" 37 self.url = "https://www.st.com" + self._url 38 """The full URL that the file was downloaded from.""" 39 self.md_url = "https://www.st.com" + self._md_url if self._md_url else None 40 """The full URL of the zipped Markdown version or `None` if there is none."""
data: dict
42 @property 43 def data(self) -> dict: 44 """ 45 A dictionary uniquely identifying this document version in a similar 46 format to the one used by the STMicro homepage. This can be used to 47 keep track of which documents have already been downloaded and which 48 have been updated upstream. 49 """ 50 return { 51 "title": self._title, 52 "localizedDescriptions": {"en": self._description}, 53 "localizedLinks": {"en": self._url}, 54 "version": self._version, 55 "latestUpdate": self._update, 56 "resourceType": self._type, 57 }
A dictionary uniquely identifying this document version in a similar format to the one used by the STMicro homepage. This can be used to keep track of which documents have already been downloaded and which have been updated upstream.
def
store_pdf(self, path: str, overwrite: bool = False) -> bool:
59 def store_pdf(self, path: str, overwrite: bool = False) -> bool: 60 """Download the PDF file to the path, optionally overwriting it.""" 61 return download_file(self.url, path, overwrite=overwrite)
Download the PDF file to the path, optionally overwriting it.
def
store_markdown(self, path: str, overwrite: bool = False) -> bool:
63 def store_markdown(self, path: str, overwrite: bool = False) -> bool: 64 """Download the zipped Markdown version to the path, optionally overwriting it.""" 65 return download_file(self.md_url, path, overwrite=overwrite)
Download the zipped Markdown version to the path, optionally overwriting it.
def
load_remote_info(base_dir: pathlib.Path, use_cached: bool = False) -> list[dict]:
138def load_remote_info(base_dir: Path, use_cached: bool = False) -> list[dict]: 139 info = base_dir / _remote_info 140 if use_cached and info.exists(): 141 LOGGER.debug("Loading remote info from cache") 142 docs = json.loads(info.read_text()) 143 else: 144 LOGGER.info("Downloading remote info") 145 docs = [] 146 for urls in _json_urls.values(): 147 for url in urls: 148 docs.extend(_download_json(url)["rows"]) 149 missing = [d for d in docs if "title" not in d or "version" not in d] 150 if missing: 151 LOGGER.warning( 152 "Some documents are missing title or version information and will be ignored: %s", 153 str(missing), 154 ) 155 return [d for d in docs if "title" in d and "version" in d]
def
load_local_info(base_dir: pathlib.Path) -> list[dict]:
def
store_remote_info(base_dir: pathlib.Path, docs: list[dict]):
def
store_local_info(base_dir: pathlib.Path, docs: list[dict]):
def
download_cubemx( extraction_path: pathlib.Path, with_download: bool = True, with_patch: bool = True) -> bool:
28def download_cubemx(extraction_path: Path, with_download: bool = True, with_patch: bool = True) -> bool: 29 # First check STMUpdaterDefinitions.xml from this zip 30 update_url = "https://sw-center.st.com/packs/resource/utility/updaters.zip" 31 update_url2 = "https://www.ebuc23.com/s3/stm_test/software/utility/updaters.zip" # noqa: F841 32 # Then Release="MX.6.2.0" maps to this: -win, -lin, -mac 33 cube_url = "https://sw-center.st.com/packs/resource/library/stm32cube_mx_v{}-lin.zip" 34 cube_url2 = "https://www.ebuc23.com/s3/stm_test/software/library/stm32cube_mx_v{}-lin.zip" # noqa: F841 35 36 if with_download: 37 LOGGER.info("Downloading Update Info...") 38 # try: 39 # urllib.request.urlopen(urllib.request.Request(update_url, headers=_hdr)) 40 # except: 41 # update_url = update_url2 42 # cube_url = cube_url2 43 # LOGGER.debug(update_url) 44 # time.sleep(random.randrange(0,2)) 45 46 z = zipfile.ZipFile(io.BytesIO(_dl(update_url))) 47 with io.TextIOWrapper(z.open("STMUpdaterDefinitions.xml"), encoding="utf-8") as defs: 48 version = re.search(r'Release="MX\.(.*?)"', defs.read()) 49 version = version.group(1).replace(".", "") 50 51 shutil.rmtree(extraction_path / "mcu", ignore_errors=True) 52 shutil.rmtree(extraction_path / "plugins", ignore_errors=True) 53 extraction_path.mkdir(exist_ok=True, parents=True) 54 55 LOGGER.info("Downloading Database...") 56 LOGGER.debug(cube_url.format(version)) 57 time.sleep(random.randrange(1, 6)) 58 59 z = zipfile.ZipFile(io.BytesIO(_dl(cube_url.format(version)))) 60 LOGGER.info("Extracting Database...") 61 for file in z.namelist(): 62 if any(file.startswith(prefix) for prefix in ("MX/db/mcu", "MX/db/plugins")): 63 z.extract(file, extraction_path) 64 65 LOGGER.info("Moving Database...") 66 shutil.move(extraction_path / "MX/db/mcu", extraction_path / "mcu") 67 shutil.move(extraction_path / "MX/db/plugins", extraction_path / "plugins") 68 shutil.rmtree(extraction_path / "MX", ignore_errors=True) 69 70 LOGGER.info("Normalizing file endings...") 71 for file in Path(extraction_path).glob("**/*"): 72 if str(file).endswith(".xml"): 73 with file.open("r", newline=None, encoding="utf-8", errors="replace") as rfile: 74 content = [line.rstrip() + "\n" for line in rfile.readlines()] 75 with file.open("w", encoding="utf-8") as wfile: 76 wfile.writelines(content) 77 78 if with_patch: 79 LOGGER.info("Patching Database...") 80 from . import data 81 82 return pkg_apply_patch(data, "cubemx.patch", extraction_path) 83 84 return True
def
download_cubeprog(extraction_path: pathlib.Path, with_download: bool = True) -> bool:
19def download_cubeprog(extraction_path: Path, with_download: bool = True) -> bool: 20 # com.st.stm32cube.ide.mcu.productdb.debug contains all the SVD files 21 # com.st.stm32cube.ide.mcu.externaltools.cubeprogrammer.linux64 contains the CubeProgrammer database 22 # com.st.stm32cube.common.mx contains the CubeMX database 23 base_url = "http://sw-center.st.com/stm32cubeide/updatesite1/" 24 cubeprog_id = "com.st.stm32cube.ide.mcu.externaltools.cubeprogrammer.linux64" 25 26 if with_download: 27 LOGGER.info("Downloading Update Info...") 28 with io.TextIOWrapper( 29 io.BytesIO(_dl(f"{base_url}/compositeContent.xml")), encoding="utf-8" 30 ) as compositeContent: 31 update_version = re.findall(r"child location='(\d+\.\d+\.\d+)'", compositeContent.read()) 32 update_version = sorted(update_version, key=lambda v: tuple(map(int, v.split("."))), reverse=True)[0] 33 34 artifacts = lzma.LZMADecompressor().decompress(_dl(f"{base_url}/{update_version}/artifacts.xml.xz")) 35 version = re.search(f"id='{cubeprog_id}' version='(.*?)'", artifacts.decode("utf-8")).group(1) 36 37 download_url = f"{base_url}/{update_version}/plugins/{cubeprog_id}_{version}.jar" 38 shutil.rmtree(extraction_path / "Data_Base", ignore_errors=True) 39 extraction_path.mkdir(exist_ok=True, parents=True) 40 41 LOGGER.info("Downloading Database...") 42 LOGGER.debug(download_url) 43 time.sleep(random.randrange(1, 6)) 44 45 z = zipfile.ZipFile(io.BytesIO(_dl(download_url))) 46 LOGGER.info("Extracting Database...") 47 for file in z.namelist(): 48 if file.startswith("tools/Data_Base"): 49 z.extract(file, extraction_path) 50 51 LOGGER.info("Moving Database...") 52 shutil.move(extraction_path / "tools/Data_Base", extraction_path / "Data_Base") 53 shutil.rmtree(extraction_path / "tools", ignore_errors=True) 54 55 LOGGER.info("Normalizing file endings...") 56 for file in Path(extraction_path).glob("**/*"): 57 if str(file).endswith(".xml"): 58 with file.open("r", newline=None, encoding="utf-8", errors="replace") as rfile: 59 content = [line.rstrip() + "\n" for line in rfile.readlines()] 60 with file.open("w", encoding="utf-8") as wfile: 61 wfile.writelines(content) 62 63 return True