From 3d179fe1a78fc90856f0fec50122c4688a8719fb Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Wed, 3 Sep 2014 23:03:49 +0300 Subject: [PATCH 1/5] Implement "lib search" command --- platformio/__init__.py | 1 + platformio/commands/lib.py | 41 ++++++++++++++++++++++++++++++++++++++ platformio/exception.py | 5 +++++ 3 files changed, 47 insertions(+) create mode 100644 platformio/commands/lib.py diff --git a/platformio/__init__.py b/platformio/__init__.py index aa4db29f..ecadb2c2 100644 --- a/platformio/__init__.py +++ b/platformio/__init__.py @@ -14,4 +14,5 @@ __email__ = "me@ikravets.com" __license__ = "MIT Licence" __copyright__ = "Copyright (C) 2014 Ivan Kravets" +__apiurl__ = "http://api.platformio.ikravets.com" __pkgmanifesturl__ = "http://platformio.ikravets.com/packages/manifest.json" diff --git a/platformio/commands/lib.py b/platformio/commands/lib.py new file mode 100644 index 00000000..045c4264 --- /dev/null +++ b/platformio/commands/lib.py @@ -0,0 +1,41 @@ +# Copyright (C) Ivan Kravets +# See LICENSE for details. + +from click import argument, group, echo, style, secho +from requests import get +from requests.exceptions import ConnectionError + +from platformio import __apiurl__ +from platformio.exception import APIRequestError + + +def get_api_result(query): + result = None + r = None + try: + r = get(__apiurl__ + query) + result = r.json() + except ConnectionError: + raise APIRequestError("Could not connect to PlatformIO API Service") + except ValueError: + raise APIRequestError("Invalid response: %s" % r.text) + finally: + if r: + r.close() + return result + + +@group(short_help="Library Manager") +def cli(): + pass + + +@cli.command("search", short_help="Search for library") +@argument("query") +def lib_search(query): + result = get_api_result("/lib/search?query=%s" % query) + secho("Found [ %d ] libraries:" % result['total'], + fg="green" if result['total'] else "yellow") + for item in result['items']: + echo("{name:<30} {info}".format(name=style(item['name'], fg="cyan"), + info=item['description'])) diff --git a/platformio/exception.py b/platformio/exception.py index 046ad084..f5d4b819 100644 --- a/platformio/exception.py +++ b/platformio/exception.py @@ -105,3 +105,8 @@ class GetSerialPortsError(PlatformioException): class GetLatestVersionError(PlatformioException): MESSAGE = "Can't retrieve latest PlatformIO version" + + +class APIRequestError(PlatformioException): + + MESSAGE = "[API] %s" From 855df959eec5c3d0178785dfe7a4aa97a6281ab5 Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Thu, 4 Sep 2014 18:55:56 +0300 Subject: [PATCH 2/5] Parse .pde as .ino file --- platformio/builder/tools/platformio.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/platformio/builder/tools/platformio.py b/platformio/builder/tools/platformio.py index 103ca4b5..5285309f 100644 --- a/platformio/builder/tools/platformio.py +++ b/platformio/builder/tools/platformio.py @@ -165,8 +165,9 @@ def ConvertInotoCpp(env): remove(f) tmpcpp = [] - - for item in env.Glob(join("$PROJECT_DIR", "src", "*.ino")): + items = (env.Glob(join("$PROJECT_DIR", "src", "*.ino")) + + env.Glob(join("$PROJECT_DIR", "src", "*.pde"))) + for item in items: cppfile = item.get_path()[:-3] + "cpp" if isfile(cppfile): continue From 35e7c8b3a7a6c58745d1fe613e0c96616915490b Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Thu, 4 Sep 2014 18:56:32 +0300 Subject: [PATCH 3/5] Seek for library in user's lib directory --- platformio/builder/main.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/platformio/builder/main.py b/platformio/builder/main.py index 97c0be78..07fd7725 100644 --- a/platformio/builder/main.py +++ b/platformio/builder/main.py @@ -16,7 +16,8 @@ from os.path import isdir, join from SCons.Script import (DefaultEnvironment, Exit, SConscript, SConscriptChdir, Variables) -from platformio.util import get_pioenvs_dir, get_project_dir, get_source_dir +from platformio.util import (get_lib_dir, get_pioenvs_dir, get_project_dir, + get_source_dir) # AllowSubstExceptions() @@ -57,6 +58,7 @@ DefaultEnvironment( BUILD_DIR=join("$PIOENVS_DIR", "$PIOENV"), LIBSOURCE_DIRS=[ join("$PROJECT_DIR", "lib"), + get_lib_dir(), join("$PLATFORMFW_DIR", "libraries"), ] ) From 3c030edc386511ae6b474ce6ca1c103e13748fe4 Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Thu, 4 Sep 2014 18:57:13 +0300 Subject: [PATCH 4/5] Grammar fix --- platformio/commands/show.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platformio/commands/show.py b/platformio/commands/show.py index 616a7a51..38cb4212 100644 --- a/platformio/commands/show.py +++ b/platformio/commands/show.py @@ -10,7 +10,7 @@ from platformio.pkgmanager import PackageManager from platformio.platforms.base import PlatformFactory -@command("show", short_help="Show details about an installed platforms") +@command("show", short_help="Show details about installed platforms") @argument("platform") def cli(platform): p = PlatformFactory().newPlatform(platform) From bac0a5ebb1ab47a6eee717e2e3ec19c67b16161f Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Thu, 4 Sep 2014 18:58:12 +0300 Subject: [PATCH 5/5] Implement lib commands: install, uninstall, show, update, list --- platformio/__init__.py | 3 +- platformio/commands/lib.py | 131 ++++++++++++++++++++++++++++++------- platformio/exception.py | 9 +++ platformio/libmanager.py | 75 +++++++++++++++++++++ platformio/util.py | 42 +++++++++++- 5 files changed, 234 insertions(+), 26 deletions(-) create mode 100644 platformio/libmanager.py diff --git a/platformio/__init__.py b/platformio/__init__.py index ecadb2c2..ff142955 100644 --- a/platformio/__init__.py +++ b/platformio/__init__.py @@ -1,7 +1,7 @@ # Copyright (C) Ivan Kravets # See LICENSE for details. -VERSION = (0, 7, "0-dev") +VERSION = (0, 7, "0.dev") __version__ = ".".join([str(s) for s in VERSION]) __title__ = "platformio" @@ -14,5 +14,6 @@ __email__ = "me@ikravets.com" __license__ = "MIT Licence" __copyright__ = "Copyright (C) 2014 Ivan Kravets" +# __apiurl__ = "http://127.0.0.1:8080" __apiurl__ = "http://api.platformio.ikravets.com" __pkgmanifesturl__ = "http://platformio.ikravets.com/packages/manifest.json" diff --git a/platformio/commands/lib.py b/platformio/commands/lib.py index 045c4264..fc5f1bcc 100644 --- a/platformio/commands/lib.py +++ b/platformio/commands/lib.py @@ -1,28 +1,13 @@ # Copyright (C) Ivan Kravets # See LICENSE for details. -from click import argument, group, echo, style, secho -from requests import get -from requests.exceptions import ConnectionError +from urllib import quote -from platformio import __apiurl__ -from platformio.exception import APIRequestError +from click import argument, echo, group, option, secho, style - -def get_api_result(query): - result = None - r = None - try: - r = get(__apiurl__ + query) - result = r.json() - except ConnectionError: - raise APIRequestError("Could not connect to PlatformIO API Service") - except ValueError: - raise APIRequestError("Invalid response: %s" % r.text) - finally: - if r: - r.close() - return result +from platformio.exception import LibAlreadyInstalledError +from platformio.libmanager import LibraryManager +from platformio.util import get_api_result, get_lib_dir @group(short_help="Library Manager") @@ -33,9 +18,107 @@ def cli(): @cli.command("search", short_help="Search for library") @argument("query") def lib_search(query): - result = get_api_result("/lib/search?query=%s" % query) - secho("Found [ %d ] libraries:" % result['total'], + result = get_api_result("/lib/search", dict(query=quote(query))) + secho("Found %d libraries:" % result['total'], fg="green" if result['total'] else "yellow") for item in result['items']: - echo("{name:<30} {info}".format(name=style(item['name'], fg="cyan"), - info=item['description'])) + echo("{name:<30} {description}".format( + name=style(item['name'], fg="cyan"), + description=item['description'] + )) + + +@cli.command("install", short_help="Install library") +@argument("names", nargs=-1) +@option("-v", "--version") +def lib_install_cli(names, version): + lib_install(names, version) + + +def lib_install(names, version=None): + lm = LibraryManager(get_lib_dir()) + for name in names: + echo("Installing %s library:" % style(name, fg="cyan")) + try: + if lm.install(name, version): + secho("The library '%s' has been successfully installed!" % + name, fg="green") + except LibAlreadyInstalledError: + secho("Already installed", fg="yellow") + + +@cli.command("uninstall", short_help="Uninstall libraries") +@argument("names", nargs=-1) +def lib_uninstall_cli(names): + lib_uninstall(names) + + +def lib_uninstall(names): + lm = LibraryManager(get_lib_dir()) + for name in names: + if lm.uninstall(name): + secho("The library '%s' has been successfully " + "uninstalled!" % 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) + echo("{name:<30} {description}".format( + name=style(info['name'], fg="cyan"), + description=info['description'] + )) + + +@cli.command("show", short_help="Show details about installed libraries") +@argument("name") +def lib_show(name): + lm = LibraryManager(get_lib_dir()) + info = lm.get_info(name) + secho(info['name'], fg="cyan") + echo("-" * len(info['name'])) + + if "author" in info: + _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) + echo("Author: %s" % " ".join(_data)) + + echo("Keywords: %s" % info['keywords']) + echo("Version: %s" % info['version']) + echo() + echo(info['description']) + echo() + + +@cli.command("update", short_help="Update installed libraries") +def lib_update(): + lm = LibraryManager(get_lib_dir()) + lib_names = lm.get_installed() + versions = get_api_result("/lib/version/" + ",".join(lib_names)) + + for name in lib_names: + info = lm.get_info(name) + + echo("Updating %s library:" % style(name, fg="yellow")) + + current_version = info['version'] + latest_version = versions[name] + + echo("Versions: Current=%s, Latest=%s \t " % ( + current_version, latest_version), nl=False) + + if current_version == latest_version: + echo("[%s]" % (style("Up-to-date", fg="green"))) + continue + else: + echo("[%s]" % (style("Out-of-date", fg="red"))) + + lib_uninstall([name]) + lib_install([name]) diff --git a/platformio/exception.py b/platformio/exception.py index f5d4b819..920ee521 100644 --- a/platformio/exception.py +++ b/platformio/exception.py @@ -110,3 +110,12 @@ class GetLatestVersionError(PlatformioException): class APIRequestError(PlatformioException): MESSAGE = "[API] %s" + + +class LibAlreadyInstalledError(PlatformioException): + pass + + +class LibNotInstalledError(PlatformioException): + + MESSAGE = "Library '%s' has not been installed yet" diff --git a/platformio/libmanager.py b/platformio/libmanager.py new file mode 100644 index 00000000..2fb4a185 --- /dev/null +++ b/platformio/libmanager.py @@ -0,0 +1,75 @@ +# Copyright (C) Ivan Kravets +# See LICENSE for details. + +import json +from os import listdir, makedirs, remove +from os.path import isdir, isfile, join +from shutil import rmtree +from tempfile import gettempdir + +from platformio.downloader import FileDownloader +from platformio.exception import LibAlreadyInstalledError, LibNotInstalledError +from platformio.unpacker import FileUnpacker +from platformio.util import get_api_result + + +class LibraryManager(object): + + CONFIG_NAME = "library.json" + + def __init__(self, lib_dir): + self.lib_dir = lib_dir + + @staticmethod + def download(url, dest_dir): + fd = FileDownloader(url, dest_dir) + fd.start() + return fd.get_filepath() + + @staticmethod + def unpack(pkgpath, dest_dir): + fu = FileUnpacker(pkgpath, dest_dir) + return fu.start() + + def get_installed(self): + 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) + 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 is_installed(self, name): + return isfile(join(self.lib_dir, name, self.CONFIG_NAME)) + + def install(self, name, version=None): + if self.is_installed(name): + raise LibAlreadyInstalledError() + + _lib_dir = join(self.lib_dir, name) + if not isdir(_lib_dir): + makedirs(_lib_dir) + + dlinfo = get_api_result("/lib/download/" + name, dict(version=version) + if version else None) + try: + dlpath = self.download(dlinfo['url'], gettempdir()) + self.unpack(dlpath, _lib_dir) + finally: + remove(dlpath) + + return self.is_installed(name) + + def uninstall(self, name): + if self.is_installed(name): + rmtree(join(self.lib_dir, name)) + return True + else: + raise LibNotInstalledError(name) diff --git a/platformio/util.py b/platformio/util.py index 34a1d785..63a10475 100644 --- a/platformio/util.py +++ b/platformio/util.py @@ -8,9 +8,14 @@ from platform import system, uname from subprocess import PIPE, Popen from time import sleep +from requests import get +from requests.exceptions import ConnectionError, HTTPError +from requests.utils import default_user_agent from serial import Serial -from platformio.exception import GetSerialPortsError, NotPlatformProject +from platformio import __apiurl__, __version__ +from platformio.exception import (APIRequestError, GetSerialPortsError, + NotPlatformProject) try: from configparser import ConfigParser @@ -36,6 +41,17 @@ def get_home_dir(): return expanduser("~/.platformio") +def get_lib_dir(): + try: + config = get_project_config() + if (config.has_section("platformio") and + config.has_option("platformio", "lib_dir")): + return config.get("platformio", "lib_dir") + except NotPlatformProject: + pass + return join(get_home_dir(), "lib") + + def get_source_dir(): return dirname(realpath(__file__)) @@ -96,3 +112,27 @@ def get_serialports(): else: raise GetSerialPortsError(os_name) return[{"port": p, "description": d, "hwid": h} for p, d, h in comports()] + + +def get_api_result(path, params=None): + result = None + r = None + try: + headers = {"User-Agent": "PlatformIO/%s %s" % ( + __version__, default_user_agent())} + r = get(__apiurl__ + path, params=params, headers=headers) + result = r.json() + r.raise_for_status() + except HTTPError as e: + if result and "errors" in result: + raise APIRequestError(result['errors'][0]['title']) + else: + raise APIRequestError(e) + except ConnectionError: + raise APIRequestError("Could not connect to PlatformIO API Service") + except ValueError: + raise APIRequestError("Invalid response: %s" % r.text) + finally: + if r: + r.close() + return result