forked from platformio/platformio-core
		
	
		
			
				
	
	
		
			634 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			634 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| # Copyright (c) 2014-present PlatformIO <contact@platformio.org>
 | |
| #
 | |
| # Licensed under the Apache License, Version 2.0 (the "License");
 | |
| # you may not use this file except in compliance with the License.
 | |
| # You may obtain a copy of the License at
 | |
| #
 | |
| #    http://www.apache.org/licenses/LICENSE-2.0
 | |
| #
 | |
| # Unless required by applicable law or agreed to in writing, software
 | |
| # distributed under the License is distributed on an "AS IS" BASIS,
 | |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | |
| # See the License for the specific language governing permissions and
 | |
| # limitations under the License.
 | |
| 
 | |
| # pylint: disable=too-many-branches, too-many-locals
 | |
| 
 | |
| import os
 | |
| import time
 | |
| 
 | |
| import click
 | |
| import semantic_version
 | |
| from tabulate import tabulate
 | |
| 
 | |
| from platformio import exception, fs, util
 | |
| from platformio.commands import PlatformioCLI
 | |
| from platformio.compat import dump_json_to_unicode
 | |
| from platformio.managers.lib import LibraryManager, get_builtin_libs, is_builtin_lib
 | |
| from platformio.package.manifest.parser import ManifestParserFactory
 | |
| from platformio.package.manifest.schema import ManifestSchema
 | |
| from platformio.proc import is_ci
 | |
| from platformio.project.config import ProjectConfig
 | |
| from platformio.project.exception import InvalidProjectConfError
 | |
| from platformio.project.helpers import get_project_dir, is_platformio_project
 | |
| 
 | |
| try:
 | |
|     from urllib.parse import quote
 | |
| except ImportError:
 | |
|     from urllib import quote
 | |
| 
 | |
| CTX_META_INPUT_DIRS_KEY = __name__ + ".input_dirs"
 | |
| CTX_META_PROJECT_ENVIRONMENTS_KEY = __name__ + ".project_environments"
 | |
| CTX_META_STORAGE_DIRS_KEY = __name__ + ".storage_dirs"
 | |
| CTX_META_STORAGE_LIBDEPS_KEY = __name__ + ".storage_lib_deps"
 | |
| 
 | |
| 
 | |
| def get_project_global_lib_dir():
 | |
|     return ProjectConfig.get_instance().get_optional_dir("globallib")
 | |
| 
 | |
| 
 | |
| @click.group(short_help="Library Manager")
 | |
| @click.option(
 | |
|     "-d",
 | |
|     "--storage-dir",
 | |
|     multiple=True,
 | |
|     default=None,
 | |
|     type=click.Path(
 | |
|         exists=True, file_okay=False, dir_okay=True, writable=True, resolve_path=True
 | |
|     ),
 | |
|     help="Manage custom library storage",
 | |
| )
 | |
| @click.option(
 | |
|     "-g", "--global", is_flag=True, help="Manage global PlatformIO library storage"
 | |
| )
 | |
| @click.option(
 | |
|     "-e",
 | |
|     "--environment",
 | |
|     multiple=True,
 | |
|     help=(
 | |
|         "Manage libraries for the specific project build environments "
 | |
|         "declared in `platformio.ini`"
 | |
|     ),
 | |
| )
 | |
| @click.pass_context
 | |
| def cli(ctx, **options):
 | |
|     storage_cmds = ("install", "uninstall", "update", "list")
 | |
|     # skip commands that don't need storage folder
 | |
|     if ctx.invoked_subcommand not in storage_cmds or (
 | |
|         len(ctx.args) == 2 and ctx.args[1] in ("-h", "--help")
 | |
|     ):
 | |
|         return
 | |
|     storage_dirs = list(options["storage_dir"])
 | |
|     if options["global"]:
 | |
|         storage_dirs.append(get_project_global_lib_dir())
 | |
|     if not storage_dirs:
 | |
|         if is_platformio_project():
 | |
|             storage_dirs = [get_project_dir()]
 | |
|         elif is_ci():
 | |
|             storage_dirs = [get_project_global_lib_dir()]
 | |
|             click.secho(
 | |
|                 "Warning! Global library storage is used automatically. "
 | |
|                 "Please use `platformio lib --global %s` command to remove "
 | |
|                 "this warning." % ctx.invoked_subcommand,
 | |
|                 fg="yellow",
 | |
|             )
 | |
| 
 | |
|     if not storage_dirs:
 | |
|         raise exception.NotGlobalLibDir(
 | |
|             get_project_dir(), get_project_global_lib_dir(), ctx.invoked_subcommand
 | |
|         )
 | |
| 
 | |
|     in_silence = PlatformioCLI.in_silence()
 | |
|     ctx.meta[CTX_META_PROJECT_ENVIRONMENTS_KEY] = options["environment"]
 | |
|     ctx.meta[CTX_META_INPUT_DIRS_KEY] = storage_dirs
 | |
|     ctx.meta[CTX_META_STORAGE_DIRS_KEY] = []
 | |
|     ctx.meta[CTX_META_STORAGE_LIBDEPS_KEY] = {}
 | |
|     for storage_dir in storage_dirs:
 | |
|         if not is_platformio_project(storage_dir):
 | |
|             ctx.meta[CTX_META_STORAGE_DIRS_KEY].append(storage_dir)
 | |
|             continue
 | |
|         with fs.cd(storage_dir):
 | |
|             config = ProjectConfig.get_instance(
 | |
|                 os.path.join(storage_dir, "platformio.ini")
 | |
|             )
 | |
|             config.validate(options["environment"], silent=in_silence)
 | |
|             libdeps_dir = config.get_optional_dir("libdeps")
 | |
|             for env in config.envs():
 | |
|                 if options["environment"] and env not in options["environment"]:
 | |
|                     continue
 | |
|                 storage_dir = os.path.join(libdeps_dir, env)
 | |
|                 ctx.meta[CTX_META_STORAGE_DIRS_KEY].append(storage_dir)
 | |
|                 ctx.meta[CTX_META_STORAGE_LIBDEPS_KEY][storage_dir] = config.get(
 | |
|                     "env:" + env, "lib_deps", []
 | |
|                 )
 | |
| 
 | |
| 
 | |
| @cli.command("install", short_help="Install library")
 | |
| @click.argument("libraries", required=False, nargs=-1, metavar="[LIBRARY...]")
 | |
| @click.option(
 | |
|     "--save",
 | |
|     is_flag=True,
 | |
|     help="Save installed libraries into the `platformio.ini` dependency list",
 | |
| )
 | |
| @click.option("-s", "--silent", is_flag=True, help="Suppress progress reporting")
 | |
| @click.option(
 | |
|     "--interactive", is_flag=True, help="Allow to make a choice for all prompts"
 | |
| )
 | |
| @click.option(
 | |
|     "-f", "--force", is_flag=True, help="Reinstall/redownload library if exists"
 | |
| )
 | |
| @click.pass_context
 | |
| def lib_install(  # pylint: disable=too-many-arguments
 | |
|     ctx, libraries, save, silent, interactive, force
 | |
| ):
 | |
|     storage_dirs = ctx.meta[CTX_META_STORAGE_DIRS_KEY]
 | |
|     storage_libdeps = ctx.meta.get(CTX_META_STORAGE_LIBDEPS_KEY, [])
 | |
| 
 | |
|     installed_manifests = {}
 | |
|     for storage_dir in storage_dirs:
 | |
|         if not silent and (libraries or storage_dir in storage_libdeps):
 | |
|             print_storage_header(storage_dirs, storage_dir)
 | |
|         lm = LibraryManager(storage_dir)
 | |
|         if libraries:
 | |
|             for library in libraries:
 | |
|                 pkg_dir = lm.install(
 | |
|                     library, silent=silent, interactive=interactive, force=force
 | |
|                 )
 | |
|                 installed_manifests[library] = lm.load_manifest(pkg_dir)
 | |
|         elif storage_dir in storage_libdeps:
 | |
|             builtin_lib_storages = None
 | |
|             for library in storage_libdeps[storage_dir]:
 | |
|                 try:
 | |
|                     pkg_dir = lm.install(
 | |
|                         library, silent=silent, interactive=interactive, force=force
 | |
|                     )
 | |
|                     installed_manifests[library] = lm.load_manifest(pkg_dir)
 | |
|                 except exception.LibNotFound as e:
 | |
|                     if builtin_lib_storages is None:
 | |
|                         builtin_lib_storages = get_builtin_libs()
 | |
|                     if not silent or not is_builtin_lib(builtin_lib_storages, library):
 | |
|                         click.secho("Warning! %s" % e, fg="yellow")
 | |
| 
 | |
|     if not save or not libraries:
 | |
|         return
 | |
| 
 | |
|     input_dirs = ctx.meta.get(CTX_META_INPUT_DIRS_KEY, [])
 | |
|     project_environments = ctx.meta[CTX_META_PROJECT_ENVIRONMENTS_KEY]
 | |
|     for input_dir in input_dirs:
 | |
|         config = ProjectConfig.get_instance(os.path.join(input_dir, "platformio.ini"))
 | |
|         config.validate(project_environments)
 | |
|         for env in config.envs():
 | |
|             if project_environments and env not in project_environments:
 | |
|                 continue
 | |
|             config.expand_interpolations = False
 | |
|             try:
 | |
|                 lib_deps = config.get("env:" + env, "lib_deps")
 | |
|             except InvalidProjectConfError:
 | |
|                 lib_deps = []
 | |
|             for library in libraries:
 | |
|                 if library in lib_deps:
 | |
|                     continue
 | |
|                 manifest = installed_manifests[library]
 | |
|                 try:
 | |
|                     assert library.lower() == manifest["name"].lower()
 | |
|                     assert semantic_version.Version(manifest["version"])
 | |
|                     lib_deps.append("{name}@^{version}".format(**manifest))
 | |
|                 except (AssertionError, ValueError):
 | |
|                     lib_deps.append(library)
 | |
|             config.set("env:" + env, "lib_deps", lib_deps)
 | |
|             config.save()
 | |
| 
 | |
| 
 | |
| @cli.command("uninstall", short_help="Uninstall libraries")
 | |
| @click.argument("libraries", nargs=-1, metavar="[LIBRARY...]")
 | |
| @click.pass_context
 | |
| def lib_uninstall(ctx, libraries):
 | |
|     storage_dirs = ctx.meta[CTX_META_STORAGE_DIRS_KEY]
 | |
|     for storage_dir in storage_dirs:
 | |
|         print_storage_header(storage_dirs, storage_dir)
 | |
|         lm = LibraryManager(storage_dir)
 | |
|         for library in libraries:
 | |
|             lm.uninstall(library)
 | |
| 
 | |
| 
 | |
| @cli.command("update", short_help="Update installed libraries")
 | |
| @click.argument("libraries", required=False, nargs=-1, metavar="[LIBRARY...]")
 | |
| @click.option(
 | |
|     "-c",
 | |
|     "--only-check",
 | |
|     is_flag=True,
 | |
|     help="DEPRECATED. Please use `--dry-run` instead",
 | |
| )
 | |
| @click.option(
 | |
|     "--dry-run", is_flag=True, help="Do not update, only check for the new versions"
 | |
| )
 | |
| @click.option("--json-output", is_flag=True)
 | |
| @click.pass_context
 | |
| def lib_update(ctx, libraries, only_check, dry_run, json_output):
 | |
|     storage_dirs = ctx.meta[CTX_META_STORAGE_DIRS_KEY]
 | |
|     only_check = dry_run or only_check
 | |
|     json_result = {}
 | |
|     for storage_dir in storage_dirs:
 | |
|         if not json_output:
 | |
|             print_storage_header(storage_dirs, storage_dir)
 | |
|         lm = LibraryManager(storage_dir)
 | |
| 
 | |
|         _libraries = libraries
 | |
|         if not _libraries:
 | |
|             _libraries = [manifest["__pkg_dir"] for manifest in lm.get_installed()]
 | |
| 
 | |
|         if only_check and json_output:
 | |
|             result = []
 | |
|             for library in _libraries:
 | |
|                 pkg_dir = library if os.path.isdir(library) else None
 | |
|                 requirements = None
 | |
|                 url = None
 | |
|                 if not pkg_dir:
 | |
|                     name, requirements, url = lm.parse_pkg_uri(library)
 | |
|                     pkg_dir = lm.get_package_dir(name, requirements, url)
 | |
|                 if not pkg_dir:
 | |
|                     continue
 | |
|                 latest = lm.outdated(pkg_dir, requirements)
 | |
|                 if not latest:
 | |
|                     continue
 | |
|                 manifest = lm.load_manifest(pkg_dir)
 | |
|                 manifest["versionLatest"] = latest
 | |
|                 result.append(manifest)
 | |
|             json_result[storage_dir] = result
 | |
|         else:
 | |
|             for library in _libraries:
 | |
|                 lm.update(library, only_check=only_check)
 | |
| 
 | |
|     if json_output:
 | |
|         return click.echo(
 | |
|             dump_json_to_unicode(
 | |
|                 json_result[storage_dirs[0]] if len(storage_dirs) == 1 else json_result
 | |
|             )
 | |
|         )
 | |
| 
 | |
|     return True
 | |
| 
 | |
| 
 | |
| @cli.command("list", short_help="List installed libraries")
 | |
| @click.option("--json-output", is_flag=True)
 | |
| @click.pass_context
 | |
| def lib_list(ctx, json_output):
 | |
|     storage_dirs = ctx.meta[CTX_META_STORAGE_DIRS_KEY]
 | |
|     json_result = {}
 | |
|     for storage_dir in storage_dirs:
 | |
|         if not json_output:
 | |
|             print_storage_header(storage_dirs, storage_dir)
 | |
|         lm = LibraryManager(storage_dir)
 | |
|         items = lm.get_installed()
 | |
|         if json_output:
 | |
|             json_result[storage_dir] = items
 | |
|         elif items:
 | |
|             for item in sorted(items, key=lambda i: i["name"]):
 | |
|                 print_lib_item(item)
 | |
|         else:
 | |
|             click.echo("No items found")
 | |
| 
 | |
|     if json_output:
 | |
|         return click.echo(
 | |
|             dump_json_to_unicode(
 | |
|                 json_result[storage_dirs[0]] if len(storage_dirs) == 1 else json_result
 | |
|             )
 | |
|         )
 | |
| 
 | |
|     return True
 | |
| 
 | |
| 
 | |
| @cli.command("search", short_help="Search for a library")
 | |
| @click.argument("query", required=False, nargs=-1)
 | |
| @click.option("--json-output", is_flag=True)
 | |
| @click.option("--page", type=click.INT, default=1)
 | |
| @click.option("--id", multiple=True)
 | |
| @click.option("-n", "--name", multiple=True)
 | |
| @click.option("-a", "--author", multiple=True)
 | |
| @click.option("-k", "--keyword", multiple=True)
 | |
| @click.option("-f", "--framework", multiple=True)
 | |
| @click.option("-p", "--platform", multiple=True)
 | |
| @click.option("-i", "--header", multiple=True)
 | |
| @click.option(
 | |
|     "--noninteractive",
 | |
|     is_flag=True,
 | |
|     help="Do not prompt, automatically paginate with delay",
 | |
| )
 | |
| def lib_search(query, json_output, page, noninteractive, **filters):
 | |
|     if not query:
 | |
|         query = []
 | |
|     if not isinstance(query, list):
 | |
|         query = list(query)
 | |
| 
 | |
|     for key, values in filters.items():
 | |
|         for value in values:
 | |
|             query.append('%s:"%s"' % (key, value))
 | |
| 
 | |
|     result = util.get_api_result(
 | |
|         "/v2/lib/search", dict(query=" ".join(query), page=page), cache_valid="1d"
 | |
|     )
 | |
| 
 | |
|     if json_output:
 | |
|         click.echo(dump_json_to_unicode(result))
 | |
|         return
 | |
| 
 | |
|     if result["total"] == 0:
 | |
|         click.secho(
 | |
|             "Nothing has been found by your request\n"
 | |
|             "Try a less-specific search or use truncation (or wildcard) "
 | |
|             "operator",
 | |
|             fg="yellow",
 | |
|             nl=False,
 | |
|         )
 | |
|         click.secho(" *", fg="green")
 | |
|         click.secho("For example: DS*, PCA*, DHT* and etc.\n", fg="yellow")
 | |
|         click.echo(
 | |
|             "For more examples and advanced search syntax, please use documentation:"
 | |
|         )
 | |
|         click.secho(
 | |
|             "https://docs.platformio.org/page/userguide/lib/cmd_search.html\n",
 | |
|             fg="cyan",
 | |
|         )
 | |
|         return
 | |
| 
 | |
|     click.secho(
 | |
|         "Found %d libraries:\n" % result["total"],
 | |
|         fg="green" if result["total"] else "yellow",
 | |
|     )
 | |
| 
 | |
|     while True:
 | |
|         for item in result["items"]:
 | |
|             print_lib_item(item)
 | |
| 
 | |
|         if int(result["page"]) * int(result["perpage"]) >= int(result["total"]):
 | |
|             break
 | |
| 
 | |
|         if noninteractive:
 | |
|             click.echo()
 | |
|             click.secho(
 | |
|                 "Loading next %d libraries... Press Ctrl+C to stop!"
 | |
|                 % result["perpage"],
 | |
|                 fg="yellow",
 | |
|             )
 | |
|             click.echo()
 | |
|             time.sleep(5)
 | |
|         elif not click.confirm("Show next libraries?"):
 | |
|             break
 | |
|         result = util.get_api_result(
 | |
|             "/v2/lib/search",
 | |
|             {"query": " ".join(query), "page": int(result["page"]) + 1},
 | |
|             cache_valid="1d",
 | |
|         )
 | |
| 
 | |
| 
 | |
| @cli.command("builtin", short_help="List built-in libraries")
 | |
| @click.option("--storage", multiple=True)
 | |
| @click.option("--json-output", is_flag=True)
 | |
| def lib_builtin(storage, json_output):
 | |
|     items = get_builtin_libs(storage)
 | |
|     if json_output:
 | |
|         return click.echo(dump_json_to_unicode(items))
 | |
| 
 | |
|     for storage_ in items:
 | |
|         if not storage_["items"]:
 | |
|             continue
 | |
|         click.secho(storage_["name"], fg="green")
 | |
|         click.echo("*" * len(storage_["name"]))
 | |
|         click.echo()
 | |
| 
 | |
|         for item in sorted(storage_["items"], key=lambda i: i["name"]):
 | |
|             print_lib_item(item)
 | |
| 
 | |
|     return True
 | |
| 
 | |
| 
 | |
| @cli.command("show", short_help="Show detailed info about a library")
 | |
| @click.argument("library", metavar="[LIBRARY]")
 | |
| @click.option("--json-output", is_flag=True)
 | |
| def lib_show(library, json_output):
 | |
|     lm = LibraryManager()
 | |
|     name, requirements, _ = lm.parse_pkg_uri(library)
 | |
|     lib_id = lm.search_lib_id(
 | |
|         {"name": name, "requirements": requirements},
 | |
|         silent=json_output,
 | |
|         interactive=not json_output,
 | |
|     )
 | |
|     lib = util.get_api_result("/lib/info/%d" % lib_id, cache_valid="1d")
 | |
|     if json_output:
 | |
|         return click.echo(dump_json_to_unicode(lib))
 | |
| 
 | |
|     click.secho(lib["name"], fg="cyan")
 | |
|     click.echo("=" * len(lib["name"]))
 | |
|     click.secho("#ID: %d" % lib["id"], bold=True)
 | |
|     click.echo(lib["description"])
 | |
|     click.echo()
 | |
| 
 | |
|     click.echo(
 | |
|         "Version: %s, released %s"
 | |
|         % (
 | |
|             lib["version"]["name"],
 | |
|             time.strftime("%c", util.parse_date(lib["version"]["released"])),
 | |
|         )
 | |
|     )
 | |
|     click.echo("Manifest: %s" % lib["confurl"])
 | |
|     for key in ("homepage", "repository", "license"):
 | |
|         if key not in lib or not lib[key]:
 | |
|             continue
 | |
|         if isinstance(lib[key], list):
 | |
|             click.echo("%s: %s" % (key.title(), ", ".join(lib[key])))
 | |
|         else:
 | |
|             click.echo("%s: %s" % (key.title(), lib[key]))
 | |
| 
 | |
|     blocks = []
 | |
| 
 | |
|     _authors = []
 | |
|     for author in lib.get("authors", []):
 | |
|         _data = []
 | |
|         for key in ("name", "email", "url", "maintainer"):
 | |
|             if not author[key]:
 | |
|                 continue
 | |
|             if key == "email":
 | |
|                 _data.append("<%s>" % author[key])
 | |
|             elif key == "maintainer":
 | |
|                 _data.append("(maintainer)")
 | |
|             else:
 | |
|                 _data.append(author[key])
 | |
|         _authors.append(" ".join(_data))
 | |
|     if _authors:
 | |
|         blocks.append(("Authors", _authors))
 | |
| 
 | |
|     blocks.append(("Keywords", lib["keywords"]))
 | |
|     for key in ("frameworks", "platforms"):
 | |
|         if key not in lib or not lib[key]:
 | |
|             continue
 | |
|         blocks.append(("Compatible %s" % key, [i["title"] for i in lib[key]]))
 | |
|     blocks.append(("Headers", lib["headers"]))
 | |
|     blocks.append(("Examples", lib["examples"]))
 | |
|     blocks.append(
 | |
|         (
 | |
|             "Versions",
 | |
|             [
 | |
|                 "%s, released %s"
 | |
|                 % (v["name"], time.strftime("%c", util.parse_date(v["released"])))
 | |
|                 for v in lib["versions"]
 | |
|             ],
 | |
|         )
 | |
|     )
 | |
|     blocks.append(
 | |
|         (
 | |
|             "Unique Downloads",
 | |
|             [
 | |
|                 "Today: %s" % lib["dlstats"]["day"],
 | |
|                 "Week: %s" % lib["dlstats"]["week"],
 | |
|                 "Month: %s" % lib["dlstats"]["month"],
 | |
|             ],
 | |
|         )
 | |
|     )
 | |
| 
 | |
|     for (title, rows) in blocks:
 | |
|         click.echo()
 | |
|         click.secho(title, bold=True)
 | |
|         click.echo("-" * len(title))
 | |
|         for row in rows:
 | |
|             click.echo(row)
 | |
| 
 | |
|     return True
 | |
| 
 | |
| 
 | |
| @cli.command("register", short_help="Register a new library")
 | |
| @click.argument("config_url")
 | |
| def lib_register(config_url):
 | |
|     if not config_url.startswith("http://") and not config_url.startswith("https://"):
 | |
|         raise exception.InvalidLibConfURL(config_url)
 | |
| 
 | |
|     # Validate manifest
 | |
|     ManifestSchema().load_manifest(
 | |
|         ManifestParserFactory.new_from_url(config_url).as_dict()
 | |
|     )
 | |
| 
 | |
|     result = util.get_api_result("/lib/register", data=dict(config_url=config_url))
 | |
|     if "message" in result and result["message"]:
 | |
|         click.secho(
 | |
|             result["message"],
 | |
|             fg="green" if "successed" in result and result["successed"] else "red",
 | |
|         )
 | |
| 
 | |
| 
 | |
| @cli.command("stats", short_help="Library Registry Statistics")
 | |
| @click.option("--json-output", is_flag=True)
 | |
| def lib_stats(json_output):
 | |
|     result = util.get_api_result("/lib/stats", cache_valid="1h")
 | |
| 
 | |
|     if json_output:
 | |
|         return click.echo(dump_json_to_unicode(result))
 | |
| 
 | |
|     for key in ("updated", "added"):
 | |
|         tabular_data = [
 | |
|             (
 | |
|                 click.style(item["name"], fg="cyan"),
 | |
|                 time.strftime("%c", util.parse_date(item["date"])),
 | |
|                 "https://platformio.org/lib/show/%s/%s"
 | |
|                 % (item["id"], quote(item["name"])),
 | |
|             )
 | |
|             for item in result.get(key, [])
 | |
|         ]
 | |
|         table = tabulate(
 | |
|             tabular_data,
 | |
|             headers=[click.style("RECENTLY " + key.upper(), bold=True), "Date", "URL"],
 | |
|         )
 | |
|         click.echo(table)
 | |
|         click.echo()
 | |
| 
 | |
|     for key in ("lastkeywords", "topkeywords"):
 | |
|         tabular_data = [
 | |
|             (
 | |
|                 click.style(name, fg="cyan"),
 | |
|                 "https://platformio.org/lib/search?query=" + quote("keyword:%s" % name),
 | |
|             )
 | |
|             for name in result.get(key, [])
 | |
|         ]
 | |
|         table = tabulate(
 | |
|             tabular_data,
 | |
|             headers=[
 | |
|                 click.style(
 | |
|                     ("RECENT" if key == "lastkeywords" else "POPULAR") + " KEYWORDS",
 | |
|                     bold=True,
 | |
|                 ),
 | |
|                 "URL",
 | |
|             ],
 | |
|         )
 | |
|         click.echo(table)
 | |
|         click.echo()
 | |
| 
 | |
|     for key, title in (("dlday", "Today"), ("dlweek", "Week"), ("dlmonth", "Month")):
 | |
|         tabular_data = [
 | |
|             (
 | |
|                 click.style(item["name"], fg="cyan"),
 | |
|                 "https://platformio.org/lib/show/%s/%s"
 | |
|                 % (item["id"], quote(item["name"])),
 | |
|             )
 | |
|             for item in result.get(key, [])
 | |
|         ]
 | |
|         table = tabulate(
 | |
|             tabular_data,
 | |
|             headers=[click.style("FEATURED: " + title.upper(), bold=True), "URL"],
 | |
|         )
 | |
|         click.echo(table)
 | |
|         click.echo()
 | |
| 
 | |
|     return True
 | |
| 
 | |
| 
 | |
| def print_storage_header(storage_dirs, storage_dir):
 | |
|     if storage_dirs and storage_dirs[0] != storage_dir:
 | |
|         click.echo("")
 | |
|     click.echo(
 | |
|         click.style("Library Storage: ", bold=True)
 | |
|         + click.style(storage_dir, fg="blue")
 | |
|     )
 | |
| 
 | |
| 
 | |
| def print_lib_item(item):
 | |
|     click.secho(item["name"], fg="cyan")
 | |
|     click.echo("=" * len(item["name"]))
 | |
|     if "id" in item:
 | |
|         click.secho("#ID: %d" % item["id"], bold=True)
 | |
|     if "description" in item or "url" in item:
 | |
|         click.echo(item.get("description", item.get("url", "")))
 | |
|     click.echo()
 | |
| 
 | |
|     for key in ("version", "homepage", "license", "keywords"):
 | |
|         if key not in item or not item[key]:
 | |
|             continue
 | |
|         if isinstance(item[key], list):
 | |
|             click.echo("%s: %s" % (key.title(), ", ".join(item[key])))
 | |
|         else:
 | |
|             click.echo("%s: %s" % (key.title(), item[key]))
 | |
| 
 | |
|     for key in ("frameworks", "platforms"):
 | |
|         if key not in item:
 | |
|             continue
 | |
|         click.echo(
 | |
|             "Compatible %s: %s"
 | |
|             % (
 | |
|                 key,
 | |
|                 ", ".join(
 | |
|                     [i["title"] if isinstance(i, dict) else i for i in item[key]]
 | |
|                 ),
 | |
|             )
 | |
|         )
 | |
| 
 | |
|     if "authors" in item or "authornames" in item:
 | |
|         click.echo(
 | |
|             "Authors: %s"
 | |
|             % ", ".join(
 | |
|                 item.get(
 | |
|                     "authornames", [a.get("name", "") for a in item.get("authors", [])]
 | |
|                 )
 | |
|             )
 | |
|         )
 | |
| 
 | |
|     if "__src_url" in item:
 | |
|         click.secho("Source: %s" % item["__src_url"])
 | |
|     click.echo()
 |