diff --git a/.github/workflows/build-images.yml b/.github/workflows/build-images.yml index 64022c0..813f89c 100644 --- a/.github/workflows/build-images.yml +++ b/.github/workflows/build-images.yml @@ -10,13 +10,18 @@ on: jobs: build-images: - runs-on: ubuntu-20.04 + runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v2 + - run: python3 -m venv build_images_venv + - name: Activate virtualenv + run: | + . build_images_venv/bin/activate + echo PATH=$PATH >> $GITHUB_ENV - run: python3 -mpip install -r requirements.txt - run: git clone --depth=1 https://github.com/adafruit/Adafruit_Learning_System_Guides learn - run: env LEARN_GUIDE_REPO=learn/ python3 create_requirement_images.py - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@v4 with: name: images path: generated_images diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml index 5c5782d..b004682 100644 --- a/.github/workflows/pre-commit.yml +++ b/.github/workflows/pre-commit.yml @@ -13,5 +13,8 @@ jobs: runs-on: ubuntu-20.04 steps: - uses: actions/checkout@v2 - - uses: actions/setup-python@v2 + - name: Set up Python 3.10 + uses: actions/setup-python@v4 + with: + python-version: "3.10" - uses: pre-commit/action@v2.0.2 diff --git a/.gitignore b/.gitignore index 7515470..c4c0d82 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,18 @@ __pycache__ latest_bundle_data.json latest_bundle_tag.json +latest_community_bundle_data.json +latest_community_bundle_tag.json generated_images + +# Virtual environment-specific files +.env +.venv + +# MacOS-specific files +*.DS_Store + +# IDE-specific files +.idea +.vscode +*~ diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 8e87341..ddf2cd3 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -4,11 +4,11 @@ repos: - repo: https://github.com/python/black - rev: 20.8b1 + rev: 22.3.0 hooks: - id: black - repo: https://github.com/fsfe/reuse-tool - rev: v0.12.1 + rev: v1.1.2 hooks: - id: reuse - repo: https://github.com/pre-commit/pre-commit-hooks @@ -18,7 +18,7 @@ repos: - id: end-of-file-fixer - id: trailing-whitespace - repo: https://github.com/pycqa/pylint - rev: pylint-2.7.1 + rev: v3.2.7 hooks: - id: pylint name: pylint (library code) diff --git a/README.md b/README.md index 6015add..dd644b5 100644 --- a/README.md +++ b/README.md @@ -16,3 +16,25 @@ With that pointed at a learn guide repo you can run: python3 create_requirement_images.py ``` It will create images in the `generated_images` directory. + +### Generate Single Learn Guide Image + +```shell +python3 create_requirement_images.py learn --guide [Learn Guide Name] +# OR +python3 create_requirement_images.py learn -g [Learn Guide Name] +``` + +### Generate Single Library Bundle Example Image +```shell +python3 create_requirement_images.py bundle [path to example].py +# e.g. +python3 create_requirement_images.py bundle Adafruit_CircuitPythonBundle/libraries/helpers/wiz/wiz_buttons_controller.py +``` + +### Help Command +The help command will list all possible commands and arguments. + +```shell +python3 create_requirement_images.py --help +``` diff --git a/create_requirement_images.py b/create_requirement_images.py index 08faf38..e52fcf2 100755 --- a/create_requirement_images.py +++ b/create_requirement_images.py @@ -14,6 +14,7 @@ import click from PIL import Image, ImageDraw, ImageFont +from get_imports import SHOWN_FILETYPES from get_imports import ( get_libs_for_project, @@ -22,6 +23,7 @@ get_files_for_example, get_learn_guide_cp_projects, ) +from settings_required import settings_required os.makedirs("generated_images", exist_ok=True) @@ -36,11 +38,11 @@ TEXT_COLOR = "#B0B0B0" HIDDEN_TEXT_COLOR = "#808080" -SHOWN_FILETYPES = ["py", "mpy", "bmp", "pcf", "bdf", "wav", "mp3", "json", "txt"] +with open("latest_bundle_data.json", "r", encoding="utf-8") as f: + bundle_data = json.load(f) -f = open("latest_bundle_data.json", "r") -bundle_data = json.load(f) -f.close() +with open("latest_community_bundle_data.json", "r", encoding="utf-8") as f: + community_bundle_data = json.load(f) def asset_path(asset_name): @@ -67,24 +69,32 @@ def asset_path(asset_name): "py": file_icon, "mpy": file_icon, "txt": file_empty_icon, + "toml": file_icon, "bmp": file_image_icon, + "png": file_image_icon, + "jpg": file_image_icon, "wav": file_music_icon, "mp3": file_music_icon, + "mid": file_music_icon, "pcf": file_font_icon, "bdf": file_font_icon, + "csv": file_empty_icon, "json": file_icon, + "license": file_empty_icon, } # If this is not done, the images fail to load in the subprocesses. -for file_icon in FILE_TYPE_ICON_MAP.values(): - file_icon.load() +for _file_icon in FILE_TYPE_ICON_MAP.values(): + _file_icon.load() def generate_requirement_image( project_files, libs, image_name -): # pylint: disable=too-many-statements +): # pylint: disable=too-many-statements, too-many-locals """Generate a single requirement image""" + context = {"added_settings_toml": False} + def make_line( requirement_name, position=(0, 0), icon=None, hidden=False, triangle_icon=None ): # pylint: disable=too-many-branches @@ -157,76 +167,130 @@ def make_line( font=font, ) - def make_header(position, project_files): + def make_header(position, project_files, files_and_libs): + # pylint: disable=too-many-locals, too-many-branches # Static files - make_line("CIRCUITPY", position) + make_line( + "CIRCUITPY", + (position[0] + INDENT_SIZE, position[1]), + triangle_icon=down_triangle, + ) make_line( ".fseventsd", - (position[0] + INDENT_SIZE, position[1] + (LINE_SPACING * 1)), + (position[0] + INDENT_SIZE * 2, position[1] + (LINE_SPACING * 1)), hidden=True, triangle_icon=right_triangle, ) make_line( ".metadata_never_index", - (position[0] + INDENT_SIZE, position[1] + (LINE_SPACING * 2)), + (position[0] + INDENT_SIZE * 2, position[1] + (LINE_SPACING * 2)), icon=file_empty_hidden_icon, hidden=True, ) make_line( ".Trashes", - (position[0] + INDENT_SIZE, position[1] + (LINE_SPACING * 3)), + (position[0] + INDENT_SIZE * 2, position[1] + (LINE_SPACING * 3)), icon=file_empty_hidden_icon, hidden=True, ) make_line( "boot_out.txt", - (position[0] + INDENT_SIZE, position[1] + (LINE_SPACING * 4)), + (position[0] + INDENT_SIZE * 2, position[1] + (LINE_SPACING * 4)), ) make_line( "code.py", - (position[0] + INDENT_SIZE, position[1] + (LINE_SPACING * 5)), + (position[0] + INDENT_SIZE * 2, position[1] + (LINE_SPACING * 5)), icon=file_icon, ) + # Add settings.toml if it's needed + if settings_required(files_and_libs): + make_line( + "settings.toml", + (position[0] + INDENT_SIZE * 2, position[1] + (LINE_SPACING * 6)), + icon=file_icon, + ) + context["added_settings_toml"] = True + + if project_files: + print(image_name) + print(project_files) + print("=============") + # dynamic files from project dir in learn guide repo rows_added = 0 project_files_to_draw = [] - project_folders_to_draw = [] + project_folders_to_draw = {} for cur_file in project_files: - if "." in cur_file[-5:]: - cur_extension = cur_file.split(".")[-1] - if cur_extension in SHOWN_FILETYPES: - project_files_to_draw.append(cur_file) - else: - project_folders_to_draw.append(cur_file) - + # string for individual file + if isinstance(cur_file, str): + if "." in cur_file[-5:]: + cur_extension = cur_file.split(".")[-1] + if cur_extension in SHOWN_FILETYPES: + project_files_to_draw.append(cur_file) + # tuple for directory + elif isinstance(cur_file, tuple): + if ".circuitpython.skip-screenshot" not in cur_file[1]: + project_folders_to_draw[cur_file[0]] = cur_file[1] + + begin_y_offset = 7 if context["added_settings_toml"] else 6 for i, file in enumerate(sorted(project_files_to_draw)): cur_file_extension = file.split(".")[-1] cur_file_icon = FILE_TYPE_ICON_MAP.get(cur_file_extension, file_empty_icon) + make_line( file, - (position[0] + INDENT_SIZE, position[1] + (LINE_SPACING * (6 + i))), + ( + position[0] + INDENT_SIZE * 2, + position[1] + (LINE_SPACING * (begin_y_offset + i)), + ), icon=cur_file_icon, ) rows_added += 1 - for i, file in enumerate(sorted(project_folders_to_draw)): + extra_rows = 0 + for i, file in enumerate(sorted(project_folders_to_draw.keys())): + if len(project_folders_to_draw[file]) > 0: + triangle_to_use = down_triangle + else: + triangle_to_use = right_triangle make_line( file, ( - position[0] + INDENT_SIZE, - position[1] + (LINE_SPACING * (6 + i + len(project_files_to_draw))), + position[0] + INDENT_SIZE * 2, + position[1] + + ( + LINE_SPACING + * (begin_y_offset + i + len(project_files_to_draw) + extra_rows) + ), ), - triangle_icon=right_triangle, + triangle_icon=triangle_to_use, ) rows_added += 1 + for sub_file in sorted(project_folders_to_draw[file]): + extra_rows += 1 + cur_file_extension = sub_file.split(".")[-1] + cur_file_icon = FILE_TYPE_ICON_MAP.get(cur_file_extension, folder_icon) + triangle_icon = None + if cur_file_icon == folder_icon: + triangle_icon = right_triangle + make_line( + sub_file, + ( + position[0] + INDENT_SIZE * 3, + position[1] + (LINE_SPACING * (begin_y_offset + rows_added)), + ), + triangle_icon=triangle_icon, + icon=cur_file_icon, + ) + rows_added += 1 make_line( "lib", ( - position[0] + INDENT_SIZE, - position[1] + (LINE_SPACING * (5 + rows_added + 1)), + position[0] + INDENT_SIZE * 2, + position[1] + (LINE_SPACING * ((begin_y_offset - 1) + rows_added + 1)), ), triangle_icon=down_triangle, ) @@ -260,10 +324,23 @@ def get_dependencies(libraries): lib_name = libraries_to_check[0] del libraries_to_check[0] - lib_obj = bundle_data[lib_name] + if lib_name in bundle_data: + lib_obj = bundle_data[lib_name] + bundle_used = bundle_data + elif lib_name in community_bundle_data: + lib_obj = community_bundle_data[lib_name] + bundle_used = community_bundle_data + else: + # handle lib that is not in any known bundle + if "." in lib_name: + file_list.add(lib_name) + else: + package_list.add(lib_name) + continue + for dep_name in lib_obj["dependencies"]: libraries_to_check.append(dep_name) - dep_obj = bundle_data[dep_name] + dep_obj = bundle_used[dep_name] if dep_obj["package"]: package_list.add(dep_name) else: @@ -280,6 +357,20 @@ def sort_libraries(libraries): package_list, file_list = get_dependencies(libraries) return sorted(package_list) + sorted(file_list) + def count_files(files_list): + _count = 0 + for _file in files_list: + # string for individual file + if isinstance(_file, str): + _count += 1 + + # tuple for directory + elif isinstance(_file, tuple): + if ".circuitpython.skip-screenshot" not in _file[1]: + _count += 1 + _count += len(_file[1]) + return _count + def make_libraries(libraries, position): for i, lib_name in enumerate(libraries): @@ -288,11 +379,50 @@ def make_libraries(libraries, position): triangle_icon = right_triangle make_line( lib_name, - (position[0] + INDENT_SIZE * 2, position[1] + (LINE_SPACING * i)), + ( + position[0] + INDENT_SIZE * 3, + position[1] + + ( + LINE_SPACING + * (i + (1 if context["added_settings_toml"] else 0)) + ), + ), triangle_icon=triangle_icon, ) + def make_sd_dir(position): + make_line( + "sd", + position, + triangle_icon=right_triangle, + ) + + def filter_custom_project_libs(project_file_set): + """ + Find and remove the custom project lib folder. + Returns a tuple with the contents of the custom project lib folder + which will in turn get included in the libraries list that the + tool uses to generate the "main" lib folder in the screenshot. + """ + _custom_libs = tuple() + remove_files = [] + for file in project_file_set: + if not isinstance(file, tuple): + continue + if file[0] == "lib": + _custom_libs = file[1] + remove_files.append(file) + for file in remove_files: + project_file_set.remove(file) + return _custom_libs + + custom_libs = filter_custom_project_libs(project_files) + libs_list = list(libs) + libs_list.extend(custom_libs) + libs = set(libs_list) final_list_to_render = sort_libraries(libs) + if settings_required(final_list_to_render): + context["added_settings_toml"] = True if "code.py" in project_files: project_files.remove("code.py") @@ -300,29 +430,44 @@ def make_libraries(libraries, position): if "main.py" in project_files: project_files.remove("main.py") - project_files_count = len(project_files) + project_files_count = count_files(project_files) image_height = ( PADDING * 2 + 7 * LINE_SPACING + len(final_list_to_render) * LINE_SPACING + (project_files_count) * LINE_SPACING + + (1 if context["added_settings_toml"] else 0) * LINE_SPACING + + 1 * LINE_SPACING # /sd/ dir ) img = Image.new("RGB", (OUT_WIDTH, image_height), "#303030") draw = ImageDraw.Draw(img) make_background_highlights( - 7 + len(final_list_to_render) + project_files_count, + 7 + + len(final_list_to_render) + + project_files_count + + (1 if context["added_settings_toml"] else 0) + + 1, # for /sd/ dir offset=(PADDING, PADDING), ) + print(f"fltr: {final_list_to_render}") + make_header((PADDING, PADDING), project_files, final_list_to_render) + _libraries_position = ( + PADDING, + PADDING + (LINE_SPACING * (7 + project_files_count)), + ) + make_libraries(final_list_to_render, _libraries_position) - make_header((PADDING, PADDING), project_files) - make_libraries( - final_list_to_render, - (PADDING, PADDING + (LINE_SPACING * (7 + project_files_count))), + _sd_dir_position = ( + 76, + PADDING + + (LINE_SPACING * (7 + project_files_count + len(final_list_to_render))) + + (1 if context["added_settings_toml"] else 0) * LINE_SPACING, ) + make_sd_dir(_sd_dir_position) - img.save("generated_images/{}.png".format(image_name)) + img.save(f"generated_images/{image_name}.png") def generate_learn_requirement_image( # pylint: disable=invalid-name @@ -356,13 +501,20 @@ def cli(ctx): @cli.command() -def learn(): +@click.option( + "-g", "--guide", help="Guide Name of a single Learn Guide to generate an image for." +) +def learn(guide=None): """Generate images for a learn-style repo""" - with Pool() as pool: - for _ in pool.imap( - generate_learn_requirement_image, get_learn_guide_cp_projects() - ): - pass + if guide is None: + with Pool() as pool: + for _ in pool.imap( + generate_learn_requirement_image, get_learn_guide_cp_projects() + ): + pass + else: + print(f"generating image for single guide: {guide}") + generate_learn_requirement_image(guide) @cli.command() diff --git a/get_imports.py b/get_imports.py index 3ea6cab..e4de803 100644 --- a/get_imports.py +++ b/get_imports.py @@ -12,29 +12,50 @@ import findimports import requests -BUNDLE_DATA = "latest_bundle_data.json" -BUNDLE_TAG = "latest_bundle_tag.json" + +ADAFRUIT_BUNDLE_DATA = "latest_bundle_data.json" +ADAFRUIT_BUNDLE_TAG = "latest_bundle_tag.json" + +COMMUNITY_BUNDLE_DATA = "latest_community_bundle_data.json" +COMMUNITY_BUNDLE_TAG = "latest_community_bundle_tag.json" + +ADAFRUIT_BUNDLE_S3_URL = "https://adafruit-circuit-python.s3.amazonaws.com/bundles/adafruit/adafruit-circuitpython-bundle-{tag}.json" # pylint: disable=line-too-long +COMMUNITY_BUNDLE_S3_URL = "https://adafruit-circuit-python.s3.amazonaws.com/bundles/community/circuitpython-community-bundle-{tag}.json" # pylint: disable=line-too-long + +SUBDIRECTORY_FILECOUNT_LIMIT = 10 LEARN_GUIDE_REPO = os.environ.get( "LEARN_GUIDE_REPO", "../Adafruit_Learning_System_Guides/" ) -SHOWN_FILETYPES = ["py", "mpy", "bmp", "pcf", "bdf", "wav", "mp3", "json", "txt"] +SHOWN_FILETYPES = [ + "py", + "mpy", + "txt", + "toml", + "bmp", + "png", + "jpg", + "wav", + "mp3", + "mid", + "pcf", + "bdf", + "csv", + "json", + "license", +] SHOWN_FILETYPES_EXAMPLE = [s for s in SHOWN_FILETYPES if s != "py"] -def get_bundle(tag): - """Download the given bundle's data to BUNDLE_DATA""" - url = f"https://adafruit-circuit-python.s3.amazonaws.com/bundles/adafruit/adafruit-circuitpython-bundle-{tag}.json" # pylint: disable=line-too-long - print(f"get bundle metadata from {url}") - r = requests.get(url) - with open(BUNDLE_DATA, "wb") as bundle_file: +def get_bundle(bundle_url, bundle_data_file): + """Download the Adafruit and Community bundles data""" + print(f"get bundle metadata from {bundle_url}") + r = requests.get(bundle_url) + with open(bundle_data_file, "wb") as bundle_file: bundle_file.write(r.content) -LATEST_BUNDLE_VERSION = "" - - def get_latest_release_from_url(url): """ Find the tag name of the latest release by using HTTP HEAD and decoding the redirect. @@ -52,44 +73,40 @@ def get_latest_release_from_url(url): return tag -def get_latest_tag(): +def get_latest_tag(repo_url): """ Find the value of the latest tag for the Adafruit CircuitPython library bundle. :return: The most recent tag value for the project. """ - global LATEST_BUNDLE_VERSION # pylint: disable=global-statement - if LATEST_BUNDLE_VERSION == "": - LATEST_BUNDLE_VERSION = get_latest_release_from_url( - "https://github.com/adafruit/Adafruit_CircuitPython_Bundle/releases/latest" - ) - return LATEST_BUNDLE_VERSION + + return get_latest_release_from_url(repo_url) -def ensure_latest_bundle(): +def ensure_latest_bundle(bundle_url, bundle_s3_url, bundle_tag_file, bundle_data_file): """ Ensure that there's a copy of the latest library bundle available so circup can check the metadata contained therein. """ print("Checking for library updates.") - tag = get_latest_tag() + tag = get_latest_tag(bundle_url) old_tag = "0" - if os.path.isfile(BUNDLE_TAG): - with open(BUNDLE_TAG, encoding="utf-8") as data: + if os.path.isfile(bundle_tag_file): + with open(bundle_tag_file, encoding="utf-8") as data: try: old_tag = json.load(data)["tag"] except json.decoder.JSONDecodeError as _: # Sometimes (why?) the JSON file becomes corrupt. In which case # log it and carry on as if setting up for first time. - print(f"Could not parse {BUNDLE_TAG:r}") + print(f"Could not parse {bundle_tag_file:r}") if tag > old_tag: print(f"New version available {tag}.") try: - get_bundle(tag) - with open(BUNDLE_TAG, "w", encoding="utf-8") as data: + get_bundle(bundle_s3_url.replace("{tag}", tag), bundle_data_file) + with open(bundle_tag_file, "w", encoding="utf-8") as data: json.dump({"tag": tag}, data) except requests.exceptions.HTTPError as _: - # See #20 for reason this this + # See #20 for reason this print( ( "There was a problem downloading the bundle. " @@ -101,43 +118,87 @@ def ensure_latest_bundle(): print(f"Current library bundle up to date {tag}") -ensure_latest_bundle() +ensure_latest_bundle( + "https://github.com/adafruit/Adafruit_CircuitPython_Bundle/releases/latest", + ADAFRUIT_BUNDLE_S3_URL, + ADAFRUIT_BUNDLE_TAG, + ADAFRUIT_BUNDLE_DATA, +) +ensure_latest_bundle( + "https://github.com/adafruit/CircuitPython_Community_Bundle/releases/latest", + COMMUNITY_BUNDLE_S3_URL, + COMMUNITY_BUNDLE_TAG, + COMMUNITY_BUNDLE_DATA, +) -with open("latest_bundle_data.json", "r") as f: +with open(ADAFRUIT_BUNDLE_DATA, "r", encoding="utf-8") as f: bundle_data = json.load(f) +with open(COMMUNITY_BUNDLE_DATA, "r", encoding="utf-8") as f: + community_bundle_data = json.load(f) + def get_files_for_project(project_name): """Get the set of files for a learn project""" found_files = set() - project_dir = "{}/{}/".format(LEARN_GUIDE_REPO, project_name) - for file in os.listdir(project_dir): + project_dir = f"{LEARN_GUIDE_REPO}/{project_name}/" + + full_tree = os.walk(project_dir) + root_level = next(full_tree) + + for file in root_level[2]: if "." in file: cur_extension = file.split(".")[-1] if cur_extension in SHOWN_FILETYPES: # print(file) found_files.add(file) - else: - # add dir - found_files.add(file) + + for _dir in root_level[1]: + dir_tuple = (_dir, tuple()) + for cur_tuple in os.walk(project_dir): + if cur_tuple[0].split("/")[-1] == _dir: + for _sub_dir in cur_tuple[1]: + dir_tuple = (dir_tuple[0], dir_tuple[1] + (_sub_dir,)) + if len(cur_tuple[2]) < SUBDIRECTORY_FILECOUNT_LIMIT: + for _sub_file in cur_tuple[2]: + dir_tuple = (dir_tuple[0], dir_tuple[1] + (_sub_file,)) + + # e.g. ("dir_name", ("file_1.txt", "file_2.txt")) + found_files.add(dir_tuple) return found_files def get_libs_for_project(project_name): + # pylint: disable=too-many-nested-blocks """Get the set of libraries for a learn project""" found_libs = set() found_imports = [] - project_dir = "{}{}/".format(LEARN_GUIDE_REPO, project_name) + project_dir = f"{LEARN_GUIDE_REPO}{project_name}/" for file in os.listdir(project_dir): if file.endswith(".py"): - found_imports = findimports.find_imports("{}{}".format(project_dir, file)) - + found_imports = findimports.find_imports(f"{project_dir}{file}") for cur_import in found_imports: cur_lib = cur_import.name.split(".")[0] - if cur_lib in bundle_data: + if cur_lib in bundle_data or cur_lib in community_bundle_data: found_libs.add(cur_lib) + # findimports returns import name in the form of "foo.bar.*" + if cur_import.name.endswith(".*"): + filepath = os.path.join( + project_dir, + os.path.join(*cur_import.name[:-2].split(".")) + ".py", + ) + if os.path.exists(filepath): + second_level_imports = findimports.find_imports(filepath) + for cur_second_level_import in second_level_imports: + cur_lib = cur_second_level_import.name.split(".")[0] + if ( + cur_lib in bundle_data + or cur_lib in community_bundle_data + ): + found_libs.add(cur_lib) + return found_libs @@ -145,15 +206,34 @@ def get_files_for_example(example_path): """Get the set of files for a library example""" found_files = set(("code.py",)) example_dir = os.path.dirname(example_path) - for file in os.listdir(example_dir): + + full_tree = os.walk(example_dir) + root_level = next(full_tree) + + for file in root_level[2]: if "." in file: cur_extension = file.split(".")[-1] if cur_extension in SHOWN_FILETYPES_EXAMPLE: # print(file) found_files.add(file) - else: - # add dir - found_files.add(file) + + for _dir in root_level[1]: + dir_tuple = (_dir, tuple()) + for cur_tuple in os.walk(example_dir): + if cur_tuple[0].split("/")[-1] == _dir: + for _sub_dir in cur_tuple[1]: + dir_tuple = (dir_tuple[0], dir_tuple[1] + (_sub_dir,)) + for _sub_file in cur_tuple[2]: + if _sub_file.split(".")[-1] in SHOWN_FILETYPES_EXAMPLE: + dir_tuple = (dir_tuple[0], dir_tuple[1] + (_sub_file,)) + + # e.g. ("dir_name", ("file_1.txt", "file_2.txt")) + + if ( + ".circuitpython.skip-screenshot" not in dir_tuple[1] + and len(dir_tuple[1]) > 0 + ): + found_files.add(dir_tuple) return found_files @@ -188,7 +268,7 @@ def get_learn_guide_cp_projects(): # Skip files in this folder, but handle sub-folders if ".circuitpython.skip-screenshot-here" in filenames: continue - # Do not reurse, but handle files in this folder + # Do not recurse, but handle files in this folder if ".circuitpython.skip-screenshot-sub" in filenames: del dirnames[:] diff --git a/settings_required.py b/settings_required.py new file mode 100644 index 0000000..b983f74 --- /dev/null +++ b/settings_required.py @@ -0,0 +1,23 @@ +# SPDX-FileCopyrightText: 2023 Tim C +# SPDX-License-Identifier: MIT + +""" +Utility function to check whether settings.toml file should be rendered for a project. +Based on whether it uses certain libraries or not. +""" + + +def settings_required(files_and_libs): + """ + Returns True if the project needs ot have a settings.toml file + + :param files_and_libs list: a List of all files and libraries used in the project + """ + + if "settings.toml" in files_and_libs: + # settings.toml file is already in the files so we don't need to add it again + return False + + # always show settings.toml because it is created by default when + # circuitpython is loaded, and when storage.erase_filesystem() is called + return True