/ get_imports.py
get_imports.py
1 # SPDX-FileCopyrightText: 2021 foamyguy 2 # SPDX-FileCopyrightText: 2019 Nicholas Tollervey, written for Adafruit Industries 3 # 4 # SPDX-License-Identifier: MIT 5 6 """ 7 Get the list of required libraries based on a file's imports 8 """ 9 10 import json 11 import os 12 import findimports 13 import requests 14 15 BUNDLE_DATA = "latest_bundle_data.json" 16 BUNDLE_TAG = "latest_bundle_tag.json" 17 18 LEARN_GUIDE_REPO = os.environ.get( 19 "LEARN_GUIDE_REPO", "../Adafruit_Learning_System_Guides/" 20 ) 21 22 SHOWN_FILETYPES = ["py", "mpy", "bmp", "pcf", "bdf", "wav", "mp3", "json", "txt"] 23 SHOWN_FILETYPES_EXAMPLE = [s for s in SHOWN_FILETYPES if s != "py"] 24 25 26 def get_bundle(tag): 27 """Download the given bundle's data to BUNDLE_DATA""" 28 url = f"https://adafruit-circuit-python.s3.amazonaws.com/bundles/adafruit/adafruit-circuitpython-bundle-{tag}.json" # pylint: disable=line-too-long 29 print(f"get bundle metadata from {url}") 30 r = requests.get(url) 31 with open(BUNDLE_DATA, "wb") as bundle_file: 32 bundle_file.write(r.content) 33 34 35 LATEST_BUNDLE_VERSION = "" 36 37 38 def get_latest_release_from_url(url): 39 """ 40 Find the tag name of the latest release by using HTTP HEAD and decoding the redirect. 41 42 :return: The most recent tag value for the release. 43 """ 44 45 print(f"Requesting redirect information: {url}") 46 response = requests.head(url) 47 responseurl = response.url 48 if response.is_redirect: 49 responseurl = response.headers["Location"] 50 tag = responseurl.rsplit("/", 1)[-1] 51 print(f"Tag: {tag!r}") 52 return tag 53 54 55 def get_latest_tag(): 56 """ 57 Find the value of the latest tag for the Adafruit CircuitPython library 58 bundle. 59 :return: The most recent tag value for the project. 60 """ 61 global LATEST_BUNDLE_VERSION # pylint: disable=global-statement 62 if LATEST_BUNDLE_VERSION == "": 63 LATEST_BUNDLE_VERSION = get_latest_release_from_url( 64 "https://github.com/adafruit/Adafruit_CircuitPython_Bundle/releases/latest" 65 ) 66 return LATEST_BUNDLE_VERSION 67 68 69 def ensure_latest_bundle(): 70 """ 71 Ensure that there's a copy of the latest library bundle available so circup 72 can check the metadata contained therein. 73 """ 74 print("Checking for library updates.") 75 tag = get_latest_tag() 76 old_tag = "0" 77 if os.path.isfile(BUNDLE_TAG): 78 with open(BUNDLE_TAG, encoding="utf-8") as data: 79 try: 80 old_tag = json.load(data)["tag"] 81 except json.decoder.JSONDecodeError as _: 82 # Sometimes (why?) the JSON file becomes corrupt. In which case 83 # log it and carry on as if setting up for first time. 84 print(f"Could not parse {BUNDLE_TAG:r}") 85 if tag > old_tag: 86 print(f"New version available {tag}.") 87 try: 88 get_bundle(tag) 89 with open(BUNDLE_TAG, "w", encoding="utf-8") as data: 90 json.dump({"tag": tag}, data) 91 except requests.exceptions.HTTPError as _: 92 # See #20 for reason this this 93 print( 94 ( 95 "There was a problem downloading the bundle. " 96 "Please try again in a moment." 97 ), 98 ) 99 raise 100 else: 101 print(f"Current library bundle up to date {tag}") 102 103 104 ensure_latest_bundle() 105 106 with open("latest_bundle_data.json", "r") as f: 107 bundle_data = json.load(f) 108 109 110 def get_files_for_project(project_name): 111 """Get the set of files for a learn project""" 112 found_files = set() 113 project_dir = "{}/{}/".format(LEARN_GUIDE_REPO, project_name) 114 for file in os.listdir(project_dir): 115 if "." in file: 116 cur_extension = file.split(".")[-1] 117 if cur_extension in SHOWN_FILETYPES: 118 # print(file) 119 found_files.add(file) 120 else: 121 # add dir 122 found_files.add(file) 123 return found_files 124 125 126 def get_libs_for_project(project_name): 127 """Get the set of libraries for a learn project""" 128 found_libs = set() 129 found_imports = [] 130 project_dir = "{}{}/".format(LEARN_GUIDE_REPO, project_name) 131 for file in os.listdir(project_dir): 132 if file.endswith(".py"): 133 134 found_imports = findimports.find_imports("{}{}".format(project_dir, file)) 135 136 for cur_import in found_imports: 137 cur_lib = cur_import.name.split(".")[0] 138 if cur_lib in bundle_data: 139 found_libs.add(cur_lib) 140 141 return found_libs 142 143 144 def get_files_for_example(example_path): 145 """Get the set of files for a library example""" 146 found_files = set(("code.py",)) 147 example_dir = os.path.dirname(example_path) 148 for file in os.listdir(example_dir): 149 if "." in file: 150 cur_extension = file.split(".")[-1] 151 if cur_extension in SHOWN_FILETYPES_EXAMPLE: 152 # print(file) 153 found_files.add(file) 154 else: 155 # add dir 156 found_files.add(file) 157 return found_files 158 159 160 def get_libs_for_example(example_path): 161 """Get the set of libraries for a library example""" 162 found_libs = set() 163 found_imports = [] 164 found_imports = findimports.find_imports(example_path) 165 166 for cur_import in found_imports: 167 cur_lib = cur_import.name.split(".")[0] 168 if cur_lib in bundle_data: 169 found_libs.add(cur_lib) 170 171 return found_libs 172 173 174 def get_learn_guide_cp_projects(): 175 """Get the list of all circuitpython projects, according to some heuristics""" 176 for dirpath, dirnames, filenames in os.walk(LEARN_GUIDE_REPO): 177 # Don't consider hidden directories 178 dirnames[:] = [d for d in dirnames if not d.startswith(".")] 179 180 # The top-level needs special treatment 181 if dirpath == LEARN_GUIDE_REPO: 182 continue 183 184 # Skip this folder and all subfolders 185 if ".circuitpython.skip" in filenames: 186 del dirnames[:] 187 continue 188 # Skip files in this folder, but handle sub-folders 189 if ".circuitpython.skip-here" in filenames: 190 continue 191 # Do not reurse, but handle files in this folder 192 if ".circuitpython.skip-sub" in filenames: 193 del dirnames[:] 194 195 if any(f for f in filenames if f.endswith(".py")): 196 yield os.path.relpath(dirpath, LEARN_GUIDE_REPO) 197 198 199 if __name__ == "__main__": 200 for p in get_learn_guide_cp_projects(): 201 print("PROJECT", p)