Avoid trademark issues in library.json; switch to PlatformIO Library Registry ID logic
Resolve issue #17
This commit is contained in:
+136
-74
@@ -3,11 +3,40 @@
|
||||
|
||||
import click
|
||||
|
||||
from platformio.exception import LibAlreadyInstalledError
|
||||
from platformio.exception import (LibAlreadyInstalledError,
|
||||
LibInstallDependencyError)
|
||||
from platformio.libmanager import LibraryManager
|
||||
from platformio.util import get_api_result, get_lib_dir
|
||||
|
||||
|
||||
LIBLIST_TPL = ("[{id:^14}] {name:<25} {compatibility:<30} "
|
||||
"\"{authornames}\": {description}")
|
||||
|
||||
|
||||
def echo_liblist_header():
|
||||
click.echo(LIBLIST_TPL.format(
|
||||
id=click.style("ID", fg="green"),
|
||||
name=click.style("Name", fg="cyan"),
|
||||
compatibility=click.style("Compatibility", fg="yellow"),
|
||||
authornames="Authors",
|
||||
description="Description"
|
||||
))
|
||||
click.echo("-" * 85)
|
||||
|
||||
|
||||
def echo_liblist_item(item):
|
||||
click.echo(LIBLIST_TPL.format(
|
||||
id=click.style(str(item['id']), fg="green"),
|
||||
name=click.style(item['name'], fg="cyan"),
|
||||
compatibility=click.style(
|
||||
", ".join(item['frameworks'] + item['platforms']),
|
||||
fg="yellow"
|
||||
),
|
||||
authornames=", ".join(item['authornames']),
|
||||
description=item['description']
|
||||
))
|
||||
|
||||
|
||||
@click.group(short_help="Library Manager")
|
||||
def cli():
|
||||
pass
|
||||
@@ -16,22 +45,24 @@ def cli():
|
||||
@cli.command("search", short_help="Search for library")
|
||||
@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.argument("query")
|
||||
def lib_search(query, author, keyword):
|
||||
for key, values in dict(author=author, keyword=keyword).iteritems():
|
||||
def lib_search(query, **filters):
|
||||
for key, values in filters.iteritems():
|
||||
for value in values:
|
||||
query += ' %s:"%s"' % (key, value)
|
||||
|
||||
result = get_api_result("/lib/search", dict(query=query))
|
||||
click.secho("Found %d libraries:" % result['total'],
|
||||
click.secho("Found %d libraries:\n" % result['total'],
|
||||
fg="green" if result['total'] else "yellow")
|
||||
|
||||
if result['total']:
|
||||
echo_liblist_header()
|
||||
|
||||
while True:
|
||||
for item in result['items']:
|
||||
click.echo("{name:<30} {description}".format(
|
||||
name=click.style(item['name'], fg="cyan"),
|
||||
description=item['description']
|
||||
))
|
||||
echo_liblist_item(item)
|
||||
|
||||
if int(result['page'])*int(result['perpage']) >= int(result['total']):
|
||||
break
|
||||
@@ -46,91 +77,115 @@ def lib_search(query, author, keyword):
|
||||
|
||||
|
||||
@cli.command("install", short_help="Install library")
|
||||
@click.argument("names", nargs=-1)
|
||||
@click.argument("ids", type=click.INT, nargs=-1, metavar="[LIBRARY_ID]")
|
||||
@click.option("-v", "--version")
|
||||
def lib_install_cli(names, version):
|
||||
lib_install(names, version)
|
||||
def lib_install_cli(ids, version):
|
||||
lib_install(ids, version)
|
||||
|
||||
|
||||
def lib_install(names, version=None):
|
||||
def lib_install(ids, version=None):
|
||||
lm = LibraryManager(get_lib_dir())
|
||||
for name in names:
|
||||
click.echo("Installing %s library:" % click.style(name, fg="cyan"))
|
||||
for id_ in ids:
|
||||
click.echo(
|
||||
"Installing library [ %s ]:" % click.style(str(id_), fg="green"))
|
||||
try:
|
||||
if lm.install(name, version):
|
||||
click.secho(
|
||||
"The library '%s' has been successfully installed!" %
|
||||
name, fg="green")
|
||||
info = lm.get_info(name)
|
||||
if "dependencies" in info:
|
||||
click.secho("Installing dependencies:", fg="yellow")
|
||||
lib_install(info['dependencies'])
|
||||
if not lm.install(id_, version):
|
||||
continue
|
||||
|
||||
info = lm.get_info(id_)
|
||||
click.secho(
|
||||
"The library #%s '%s' has been successfully installed!"
|
||||
% (str(id_), info['name']), fg="green")
|
||||
|
||||
if "dependencies" in info:
|
||||
click.secho("Installing dependencies:", fg="yellow")
|
||||
_dependencies = info['dependencies']
|
||||
if not isinstance(_dependencies, list):
|
||||
_dependencies = [_dependencies]
|
||||
for item in _dependencies:
|
||||
try:
|
||||
lib_install_dependency(item)
|
||||
except AssertionError:
|
||||
raise LibInstallDependencyError(str(item))
|
||||
|
||||
except LibAlreadyInstalledError:
|
||||
click.secho("Already installed", fg="yellow")
|
||||
|
||||
|
||||
def lib_install_dependency(data):
|
||||
assert isinstance(data, dict)
|
||||
query = []
|
||||
for key in data.keys():
|
||||
if key in ("authors", "frameworks", "platforms", "keywords"):
|
||||
values = data[key]
|
||||
if not isinstance(values, list):
|
||||
values = [v.strip() for v in values.split(",") if v]
|
||||
for value in values:
|
||||
query.append('%s:"%s"' % (key[:-1], value))
|
||||
elif isinstance(data[key], basestring):
|
||||
query.append('+"%s"' % data[key])
|
||||
|
||||
result = get_api_result("/lib/search", dict(query=" ".join(query)))
|
||||
assert result['total'] == 1
|
||||
lib_install([result['items'][0]['id']])
|
||||
|
||||
|
||||
@cli.command("uninstall", short_help="Uninstall libraries")
|
||||
@click.argument("names", nargs=-1)
|
||||
def lib_uninstall_cli(names):
|
||||
lib_uninstall(names)
|
||||
@click.argument("ids", type=click.INT, nargs=-1)
|
||||
def lib_uninstall_cli(ids):
|
||||
lib_uninstall(ids)
|
||||
|
||||
|
||||
def lib_uninstall_dependency(dependency):
|
||||
def lib_uninstall(ids):
|
||||
lm = LibraryManager(get_lib_dir())
|
||||
|
||||
for name in lm.get_installed():
|
||||
info = lm.get_info(name)
|
||||
if dependency in info.get("dependencies", []):
|
||||
return
|
||||
|
||||
lib_uninstall([dependency])
|
||||
|
||||
|
||||
def lib_uninstall(names):
|
||||
lm = LibraryManager(get_lib_dir())
|
||||
for name in names:
|
||||
info = lm.get_info(name)
|
||||
if lm.uninstall(name):
|
||||
# find dependencies
|
||||
if "dependencies" in info:
|
||||
for d in info['dependencies']:
|
||||
lib_uninstall_dependency(d)
|
||||
|
||||
click.secho("The library '%s' has been successfully "
|
||||
"uninstalled!" % name, fg="green")
|
||||
for id_ in ids:
|
||||
info = lm.get_info(id_)
|
||||
if lm.uninstall(id_):
|
||||
click.secho("The library #%s '%s' has been successfully "
|
||||
"uninstalled!" % (str(id_), info['name']), fg="green")
|
||||
|
||||
|
||||
@cli.command("list", short_help="List installed libraries")
|
||||
def lib_list():
|
||||
lm = LibraryManager(get_lib_dir())
|
||||
for name in lm.get_installed():
|
||||
info = lm.get_info(name)
|
||||
click.echo("{name:<30} {description}".format(
|
||||
name=click.style(info['name'], fg="cyan"),
|
||||
description=info['description']
|
||||
))
|
||||
items = lm.get_installed().values()
|
||||
if not items:
|
||||
return
|
||||
|
||||
echo_liblist_header()
|
||||
for item in items:
|
||||
item['authornames'] = [i['name'] for i in item['authors']]
|
||||
echo_liblist_item(item)
|
||||
|
||||
|
||||
@cli.command("show", short_help="Show details about installed libraries")
|
||||
@click.argument("name")
|
||||
def lib_show(name):
|
||||
@click.argument("id", type=click.INT)
|
||||
def lib_show(id):
|
||||
lm = LibraryManager(get_lib_dir())
|
||||
info = lm.get_info(name)
|
||||
info = lm.get_info(id)
|
||||
click.secho(info['name'], fg="cyan")
|
||||
click.echo("-" * len(info['name']))
|
||||
|
||||
if "author" in info:
|
||||
_authors = []
|
||||
for author in info['authors']:
|
||||
_data = []
|
||||
for k in ("name", "email"):
|
||||
if k in info['author'] and info['author'][k] is not None:
|
||||
_value = info['author'][k]
|
||||
if k == "email":
|
||||
_value = "<%s>" % _value
|
||||
_data.append(_value)
|
||||
click.echo("Author: %s" % " ".join(_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))
|
||||
click.echo("Authors: %s" % ", ".join(_authors))
|
||||
|
||||
click.echo("Keywords: %s" % info['keywords'])
|
||||
click.echo("Keywords: %s" % ", ".join(info['keywords']))
|
||||
if "frameworks" in info:
|
||||
click.echo("Frameworks: %s" % ", ".join(info['frameworks']))
|
||||
if "platforms" in info:
|
||||
click.echo("Platforms: %s" % ", ".join(info['platforms']))
|
||||
click.echo("Version: %s" % info['version'])
|
||||
click.echo()
|
||||
click.echo(info['description'])
|
||||
@@ -141,18 +196,25 @@ def lib_show(name):
|
||||
def lib_update():
|
||||
lm = LibraryManager(get_lib_dir())
|
||||
|
||||
lib_names = lm.get_installed()
|
||||
if not lib_names:
|
||||
lib_ids = [str(item['id']) for item in lm.get_installed().values()]
|
||||
if not lib_ids:
|
||||
return
|
||||
|
||||
versions = get_api_result("/lib/version/" + ",".join(lib_names))
|
||||
for name in lib_names:
|
||||
info = lm.get_info(name)
|
||||
versions = get_api_result("/lib/version/" + str(",".join(lib_ids)))
|
||||
for id_ in lib_ids:
|
||||
info = lm.get_info(int(id_))
|
||||
|
||||
click.echo("Updating %s library:" % click.style(name, fg="yellow"))
|
||||
click.echo("Updating [ %s ] %s library:" % (
|
||||
click.style(id_, fg="yellow"),
|
||||
click.style(info['name'], fg="cyan"))
|
||||
)
|
||||
|
||||
current_version = info['version']
|
||||
latest_version = versions[name]
|
||||
latest_version = versions[id_]
|
||||
|
||||
if latest_version is None:
|
||||
click.secho("Unknown library", fg="red")
|
||||
continue
|
||||
|
||||
click.echo("Versions: Current=%s, Latest=%s \t " % (
|
||||
current_version, latest_version), nl=False)
|
||||
@@ -163,8 +225,8 @@ def lib_update():
|
||||
else:
|
||||
click.echo("[%s]" % (click.style("Out-of-date", fg="red")))
|
||||
|
||||
lib_uninstall([name])
|
||||
lib_install([name])
|
||||
lib_uninstall([int(id_)])
|
||||
lib_install([int(id_)])
|
||||
|
||||
|
||||
@cli.command("register", short_help="Register new library")
|
||||
|
||||
@@ -118,4 +118,9 @@ class LibAlreadyInstalledError(PlatformioException):
|
||||
|
||||
class LibNotInstalledError(PlatformioException):
|
||||
|
||||
MESSAGE = "Library '%s' has not been installed yet"
|
||||
MESSAGE = "Library #%d has not been installed yet"
|
||||
|
||||
|
||||
class LibInstallDependencyError(PlatformioException):
|
||||
|
||||
MESSAGE = "Error has been occurred for library dependency '%s'"
|
||||
|
||||
+41
-31
@@ -2,7 +2,8 @@
|
||||
# See LICENSE for details.
|
||||
|
||||
import json
|
||||
from os import listdir, makedirs, remove
|
||||
import re
|
||||
from os import listdir, makedirs, remove, rename
|
||||
from os.path import isdir, isfile, join
|
||||
from shutil import rmtree
|
||||
from tempfile import gettempdir
|
||||
@@ -15,7 +16,7 @@ from platformio.util import get_api_result
|
||||
|
||||
class LibraryManager(object):
|
||||
|
||||
CONFIG_NAME = "library.json"
|
||||
CONFIG_NAME = ".library.json"
|
||||
|
||||
def __init__(self, lib_dir):
|
||||
self.lib_dir = lib_dir
|
||||
@@ -32,45 +33,54 @@ class LibraryManager(object):
|
||||
return fu.start()
|
||||
|
||||
def get_installed(self):
|
||||
items = []
|
||||
items = {}
|
||||
if not isdir(self.lib_dir):
|
||||
return items
|
||||
for item in listdir(self.lib_dir):
|
||||
conf_path = join(self.lib_dir, item, self.CONFIG_NAME)
|
||||
if isfile(conf_path):
|
||||
items.append(item)
|
||||
for dirname in listdir(self.lib_dir):
|
||||
conf_path = join(self.lib_dir, dirname, self.CONFIG_NAME)
|
||||
if not isfile(conf_path):
|
||||
continue
|
||||
with open(conf_path, "r") as f:
|
||||
items[dirname] = json.load(f)
|
||||
return items
|
||||
|
||||
def get_info(self, name):
|
||||
conf_path = join(self.lib_dir, name, self.CONFIG_NAME)
|
||||
if not isfile(conf_path):
|
||||
raise LibNotInstalledError(name)
|
||||
with open(conf_path, "r") as f:
|
||||
return json.load(f)
|
||||
def get_info(self, id_):
|
||||
for item in self.get_installed().values():
|
||||
if "id" in item and item['id'] == id_:
|
||||
return item
|
||||
raise LibNotInstalledError(id_)
|
||||
|
||||
def is_installed(self, name):
|
||||
return isfile(join(self.lib_dir, name, self.CONFIG_NAME))
|
||||
def is_installed(self, id_):
|
||||
try:
|
||||
return int(self.get_info(id_)['id']) == id_
|
||||
except LibNotInstalledError:
|
||||
return False
|
||||
|
||||
def install(self, name, version=None):
|
||||
if self.is_installed(name):
|
||||
def install(self, id_, version=None):
|
||||
if self.is_installed(id_):
|
||||
raise LibAlreadyInstalledError()
|
||||
|
||||
dlinfo = get_api_result("/lib/download/" + name, dict(version=version)
|
||||
if version else None)
|
||||
dlinfo = get_api_result("/lib/download/" + str(id_),
|
||||
dict(version=version) if version else None)
|
||||
dlpath = None
|
||||
tmplib_dir = join(self.lib_dir, str(id_))
|
||||
try:
|
||||
dlpath = self.download(dlinfo['url'], gettempdir())
|
||||
_lib_dir = join(self.lib_dir, name)
|
||||
if not isdir(_lib_dir):
|
||||
makedirs(_lib_dir)
|
||||
self.unpack(dlpath, _lib_dir)
|
||||
if not isdir(tmplib_dir):
|
||||
makedirs(tmplib_dir)
|
||||
self.unpack(dlpath, tmplib_dir)
|
||||
finally:
|
||||
remove(dlpath)
|
||||
if dlpath:
|
||||
remove(dlpath)
|
||||
|
||||
return self.is_installed(name)
|
||||
info = self.get_info(id_)
|
||||
rename(tmplib_dir, join(self.lib_dir, "%s_ID%d" % (
|
||||
re.sub(r"[^\da-z]+", "_", info['name'], flags=re.I), id_)))
|
||||
return True
|
||||
|
||||
def uninstall(self, name):
|
||||
if self.is_installed(name):
|
||||
rmtree(join(self.lib_dir, name))
|
||||
return True
|
||||
else:
|
||||
raise LibNotInstalledError(name)
|
||||
def uninstall(self, id_):
|
||||
for libdir, item in self.get_installed().iteritems():
|
||||
if "id" in item and item['id'] == id_:
|
||||
rmtree(join(self.lib_dir, libdir))
|
||||
return True
|
||||
raise LibNotInstalledError(id_)
|
||||
|
||||
+2
-1
@@ -137,7 +137,8 @@ def get_api_result(path, params=None, data=None):
|
||||
else:
|
||||
raise APIRequestError(e)
|
||||
except ConnectionError:
|
||||
raise APIRequestError("Could not connect to PlatformIO API Service")
|
||||
raise APIRequestError(
|
||||
"Could not connect to PlatformIO Registry Service")
|
||||
except ValueError:
|
||||
raise APIRequestError("Invalid response: %s" % r.text)
|
||||
finally:
|
||||
|
||||
Reference in New Issue
Block a user