diff --git a/circup/backends.py b/circup/backends.py index 4e2d1b7..49bd4d5 100644 --- a/circup/backends.py +++ b/circup/backends.py @@ -763,21 +763,25 @@ class DiskBackend(Backend): raise IOError("Cannot find compiled version of module.") # pylint: enable=too-many-locals,too-many-branches - def _install_module_py(self, metadata): + def _install_module_py(self, metadata, location=None): """ :param library_path library path :param metadata dictionary. """ + if location is None: + location = self.library_path + else: + location = os.path.join(self.device_location, location) source_path = metadata["path"] # Path to Python source version. if os.path.isdir(source_path): target = os.path.basename(os.path.dirname(source_path)) - target_path = os.path.join(self.library_path, target) + target_path = os.path.join(location, target) # Copy the directory. shutil.copytree(source_path, target_path) else: target = os.path.basename(source_path) - target_path = os.path.join(self.library_path, target) + target_path = os.path.join(location, target) # Copy file. shutil.copyfile(source_path, target_path) diff --git a/circup/bundle.py b/circup/bundle.py index 39532aa..08ee5d2 100644 --- a/circup/bundle.py +++ b/circup/bundle.py @@ -61,6 +61,20 @@ class Bundle: "lib", ) + def examples_dir(self, platform): + """ + This bundle's examples directory for the platform. + + :param str platform: The platform identifier (py/6mpy/...). + :return: The path to the examples directory for the platform. + """ + tag = self.current_tag + return os.path.join( + self.dir.format(platform=platform), + self.basename.format(platform=PLATFORMS[platform], tag=tag), + "examples", + ) + def requirements_for(self, library_name, toml_file=False): """ The requirements file for this library. diff --git a/circup/command_utils.py b/circup/command_utils.py index 528ba99..6eef760 100644 --- a/circup/command_utils.py +++ b/circup/command_utils.py @@ -93,6 +93,31 @@ def completion_for_install(ctx, param, incomplete): return sorted(module_names) +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 + # available_modules = get_bundle_versions(get_bundles_list(), avoid_download=True) + # module_names = {m.replace(".py", "") for m in available_modules} + # if incomplete: + # module_names = [name for name in module_names if name.startswith(incomplete)] + # return sorted(module_names) + + available_examples = get_bundle_examples(get_bundles_list(), avoid_download=True) + # with open("tmp1.py", "w") as f: + # f.write(f"data = {str(available_examples)}") + + matching_examples = [ + example_path + for example_path in available_examples.keys() + if example_path.startswith(incomplete) + ] + + return sorted(matching_examples) + + def ensure_latest_bundle(bundle): """ Ensure that there's a copy of the latest library bundle available so circup @@ -290,6 +315,63 @@ def get_bundle(bundle, tag): click.echo("\nOK\n") +def get_bundle_examples(bundles_list, avoid_download=False): + """ + Return a dictionary of metadata from examples in the all of the bundles + specified by bundles_list argument. + + :param List[Bundle] bundles_list: List of supported bundles as Bundle objects. + :param bool avoid_download: if True, download the bundle only if missing. + :return: A dictionary of metadata about the examples available in the + library bundle. + """ + all_the_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") + # with open("path_debug.txt", "a") as f: + # f.write(path + "\n") + path_examples = _get_modules_file(path, logger) + + # with open("path_modules_debug.txt", "a") as f: + # f.write(str(path_examples) + "\n----------\n") + + for lib_name, lib_metadata in path_examples.items(): + + walk_val = os.walk(lib_metadata["path"]) + # with open("path_walk_debug.txt", "a") as f: + # for thing in walk_val: + # f.write(f"{lib_name}: " + str(thing) + "\n----\n") + # f.write("\n=========================\n") + + # for _example_file in os.listdir(lib_metadata["path"]): + # all_the_examples[f"{lib_name}/{_example_file.replace('.py', '')}"] = os.path.join(lib_metadata["path"], f"{_example_file}") + + for _dir_level in os.walk(lib_metadata["path"]): + for _file in _dir_level[2]: + _parts = _dir_level[0].split(os.path.sep) + _lib_name_index = _parts.index(lib_name) + _dirs = _parts[_lib_name_index:] + if _dirs[-1] == "": + _dirs.pop(-1) + with open("dirs_parts_debug.txt", "a") as f: + f.write(f"{lib_name}: " + str(_dirs) + "\n----\n") + slug = f"{os.path.sep}".join(_dirs + [_file.replace(".py", "")]) + all_the_examples[slug] = os.path.join(_dir_level[0], _file) + + # all_the_examples[f"{lib_name}/{_example_file.replace('.py', '')}"] = os.path.join( + # lib_metadata["path"], f"{_file}") + + except NotADirectoryError: + # Bundle does not have new style examples directory + # so we cannot include its examples. + pass + return all_the_examples + + def get_bundle_versions(bundles_list, avoid_download=False): """ Returns a dictionary of metadata from modules in the latest known release diff --git a/circup/commands.py b/circup/commands.py index 74117d9..91c7b72 100644 --- a/circup/commands.py +++ b/circup/commands.py @@ -38,6 +38,8 @@ from circup.command_utils import ( get_bundles_local_dict, save_local_bundles, get_bundles_dict, + completion_for_example, + get_bundle_examples, ) @@ -347,6 +349,29 @@ def install(ctx, modules, pyext, requirement, auto, auto_file): # pragma: no co ) +@main.command() +@click.argument( + "examples", required=True, nargs=-1, shell_complete=completion_for_example +) +@click.pass_context +def example(ctx, examples): + print(f"context: {ctx}") + for example in examples: + available_examples = get_bundle_examples( + get_bundles_list(), avoid_download=True + ) + if example in available_examples: + click.echo(available_examples[example]) + ctx.obj["backend"]._install_module_py( + {"path": available_examples[example]}, location="" + ) + else: + click.secho( + f"Error: {example} was not found in any local bundle examples.", + fg="red", + ) + + # pylint: enable=too-many-arguments,too-many-locals