Add --list and --rename options to example

Added caching of example library example metadata, to speed up tab completion and --list returns

--rename option will rename output to code.py if example resolves to single file

Changed connection announcement to include board name
This commit is contained in:
Brian K. Jackson(Arakkis) 2025-01-12 07:50:34 -05:00
parent da8f6c26c5
commit bd834c4603
3 changed files with 50 additions and 9 deletions

View file

@ -950,7 +950,10 @@ class DiskBackend(Backend):
# Copy the directory. # Copy the directory.
shutil.copytree(source_path, target_path) shutil.copytree(source_path, target_path)
else: else:
target = os.path.basename(source_path) if "target_name" in metadata:
target = metadata["target_name"]
else:
target = os.path.basename(source_path)
target_path = os.path.join(location, target) target_path = os.path.join(location, target)
# Copy file. # Copy file.
shutil.copyfile(source_path, target_path) shutil.copyfile(source_path, target_path)

View file

@ -8,6 +8,7 @@ Functions called from commands in order to provide behaviors and return informat
import ctypes import ctypes
import glob import glob
import os import os
import pickle
from subprocess import check_output from subprocess import check_output
import sys import sys
@ -100,6 +101,7 @@ def completion_for_example(ctx, param, incomplete):
Returns the list of available modules for the command line tab-completion Returns the list of available modules for the command line tab-completion
with the ``circup example`` command. with the ``circup example`` command.
""" """
# pylint: disable=unused-argument, consider-iterating-dictionary # pylint: disable=unused-argument, consider-iterating-dictionary
available_examples = get_bundle_examples(get_bundles_list(), avoid_download=True) available_examples = get_bundle_examples(get_bundles_list(), avoid_download=True)
@ -319,14 +321,22 @@ def get_bundle_examples(bundles_list, avoid_download=False):
:return: A dictionary of metadata about the examples available in the :return: A dictionary of metadata about the examples available in the
library bundle. library bundle.
""" """
# pylint: disable=too-many-nested-blocks # pylint: disable=too-many-nested-blocks,too-many-locals
all_the_examples = dict() all_the_examples = dict()
bundle_examples = dict()
try: try:
for bundle in bundles_list: for bundle in bundles_list:
if not avoid_download or not os.path.isdir(bundle.lib_dir("py")): if not avoid_download or not os.path.isdir(bundle.lib_dir("py")):
ensure_latest_bundle(bundle) ensure_latest_bundle(bundle)
path = bundle.examples_dir("py") path = bundle.examples_dir("py")
meta_saved = os.path.join(path, "../bundle_examples.pickle")
if os.path.exists(meta_saved):
with open(meta_saved, "rb") as f:
bundle_examples = pickle.load(f)
all_the_examples.update(bundle_examples)
bundle_examples.clear()
continue
path_examples = _get_modules_file(path, logger) path_examples = _get_modules_file(path, logger)
for lib_name, lib_metadata in path_examples.items(): for lib_name, lib_metadata in path_examples.items():
for _dir_level in os.walk(lib_metadata["path"]): for _dir_level in os.walk(lib_metadata["path"]):
@ -337,7 +347,11 @@ def get_bundle_examples(bundles_list, avoid_download=False):
if _dirs[-1] == "": if _dirs[-1] == "":
_dirs.pop(-1) _dirs.pop(-1)
slug = f"{os.path.sep}".join(_dirs + [_file.replace(".py", "")]) slug = f"{os.path.sep}".join(_dirs + [_file.replace(".py", "")])
bundle_examples[slug] = os.path.join(_dir_level[0], _file)
all_the_examples[slug] = os.path.join(_dir_level[0], _file) all_the_examples[slug] = os.path.join(_dir_level[0], _file)
with open(meta_saved, "wb") as f:
pickle.dump(bundle_examples, f)
bundle_examples.clear()
except NotADirectoryError: except NotADirectoryError:
# Bundle does not have new style examples directory # Bundle does not have new style examples directory

View file

@ -178,8 +178,8 @@ def main( # pylint: disable=too-many-locals
else (cpy_version, board_id) else (cpy_version, board_id)
) )
click.echo( click.echo(
"Found device at {}, running CircuitPython {}.".format( "Found device {} at {}, running CircuitPython {}.".format(
device_path, cpy_version board_id, device_path, cpy_version
) )
) )
try: try:
@ -406,31 +406,55 @@ def install(
@main.command() @main.command()
@click.option("--overwrite", is_flag=True, help="Overwrite the file if it exists.") @click.option("--overwrite", is_flag=True, help="Overwrite the file if it exists.")
@click.option("--list", "-ls", "op_list", is_flag=True, help="List available examples.")
@click.option("--rename", is_flag=True, help="Install the example as code.py.")
@click.argument( @click.argument(
"examples", required=True, nargs=-1, shell_complete=completion_for_example "examples", required=False, nargs=-1, shell_complete=completion_for_example
) )
@click.pass_context @click.pass_context
def example(ctx, examples, overwrite): def example(ctx, examples, op_list, rename, overwrite):
""" """
Copy named example(s) from a bundle onto the device. Multiple examples Copy named example(s) from a bundle onto the device. Multiple examples
can be installed at once by providing more than one example name, each can be installed at once by providing more than one example name, each
separated by a space. separated by a space.
""" """
if op_list:
if examples:
click.echo("\n".join(completion_for_example(ctx, "", examples)))
else:
click.echo("Available example libraries:")
available_examples = get_bundle_examples(
get_bundles_list(), avoid_download=True
)
lib_names = {
str(key.split(os.path.sep)[0]): value
for key, value in available_examples.items()
}
click.echo("\n".join(sorted(lib_names.keys())))
return
for example_arg in examples: for example_arg in examples:
available_examples = get_bundle_examples( available_examples = get_bundle_examples(
get_bundles_list(), avoid_download=True get_bundles_list(), avoid_download=True
) )
if example_arg in available_examples: if example_arg in available_examples:
filename = available_examples[example_arg].split(os.path.sep)[-1] filename = available_examples[example_arg].split(os.path.sep)[-1]
install_metadata = {"path": available_examples[example_arg]}
# check of we are dealing with a file that needs to be pushed as code.py
# or a directory that needs to be copied raw
filename = available_examples[example_arg].split(os.path.sep)[-1]
if rename:
if os.path.isfile(available_examples[example_arg]):
filename = "code.py"
install_metadata["target_name"] = filename
if overwrite or not ctx.obj["backend"].file_exists(filename): if overwrite or not ctx.obj["backend"].file_exists(filename):
click.echo( click.echo(
f"{'Copying' if not overwrite else 'Overwriting'}: {filename}" f"{'Copying' if not overwrite else 'Overwriting'}: {filename}"
) )
ctx.obj["backend"].install_module_py( ctx.obj["backend"].install_module_py(install_metadata, location="")
{"path": available_examples[example_arg]}, location=""
)
else: else:
click.secho( click.secho(
f"File: {filename} already exists. Use --overwrite if you wish to replace it.", f"File: {filename} already exists. Use --overwrite if you wish to replace it.",