diff --git a/circup/backends.py b/circup/backends.py index cba33fa..0fc8502 100644 --- a/circup/backends.py +++ b/circup/backends.py @@ -950,7 +950,10 @@ class DiskBackend(Backend): # Copy the directory. shutil.copytree(source_path, target_path) 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) # Copy file. shutil.copyfile(source_path, target_path) diff --git a/circup/command_utils.py b/circup/command_utils.py index 00f162e..fafb66f 100644 --- a/circup/command_utils.py +++ b/circup/command_utils.py @@ -8,6 +8,7 @@ Functions called from commands in order to provide behaviors and return informat import ctypes import glob import os +import pickle from subprocess import check_output 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 with the ``circup example`` command. """ + # pylint: disable=unused-argument, consider-iterating-dictionary 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 library bundle. """ - # pylint: disable=too-many-nested-blocks + # pylint: disable=too-many-nested-blocks,too-many-locals all_the_examples = dict() + bundle_examples = dict() try: for bundle in bundles_list: if not avoid_download or not os.path.isdir(bundle.lib_dir("py")): ensure_latest_bundle(bundle) 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) for lib_name, lib_metadata in path_examples.items(): 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] == "": _dirs.pop(-1) 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) + with open(meta_saved, "wb") as f: + pickle.dump(bundle_examples, f) + bundle_examples.clear() except NotADirectoryError: # Bundle does not have new style examples directory diff --git a/circup/commands.py b/circup/commands.py index db17c15..6384a5f 100644 --- a/circup/commands.py +++ b/circup/commands.py @@ -178,8 +178,8 @@ def main( # pylint: disable=too-many-locals else (cpy_version, board_id) ) click.echo( - "Found device at {}, running CircuitPython {}.".format( - device_path, cpy_version + "Found device {} at {}, running CircuitPython {}.".format( + board_id, device_path, cpy_version ) ) try: @@ -406,31 +406,55 @@ def install( @main.command() @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( - "examples", required=True, nargs=-1, shell_complete=completion_for_example + "examples", required=False, nargs=-1, shell_complete=completion_for_example ) @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 can be installed at once by providing more than one example name, each 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: available_examples = get_bundle_examples( get_bundles_list(), avoid_download=True ) if example_arg in available_examples: 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): click.echo( f"{'Copying' if not overwrite else 'Overwriting'}: {filename}" ) - ctx.obj["backend"].install_module_py( - {"path": available_examples[example_arg]}, location="" - ) + ctx.obj["backend"].install_module_py(install_metadata, location="") else: click.secho( f"File: {filename} already exists. Use --overwrite if you wish to replace it.",