#!/home/meko/Builds/test-kivy-app/kivy_venv/bin/python3 import sys import argparse import zipfile import tempfile from shutil import rmtree, move from os import listdir, getcwd, chdir, makedirs from os.path import join, realpath, exists, isdir, expanduser, abspath, dirname try: from cBytesIO import BytesIO except ImportError: try: from BytesIO import BytesIO except ImportError: from io import BytesIO try: import requests except ImportError: print('Garden tool require requests library.') print('Try to "pip install requests" in root') sys.exit(1) try: import kivy garden_kivy_dir = abspath(join(dirname(kivy.__file__), 'garden')) except ImportError: garden_kivy_dir = None garden_system_dir = join(expanduser('~'), '.kivy', 'garden') garden_app_dir = join(realpath(getcwd()), 'libs', 'garden') class GardenTool(object): '''Garden command-line tool. ''' def main(self, argv): parser = argparse.ArgumentParser(description=self.__doc__) subparsers = parser.add_subparsers() p = subparsers.add_parser('list', help='List all the installed garden packages') p.add_argument('--app', action='store_true', help='Use the local app directory (./libs/garden)') p.add_argument('--kivy', action='store_true', help='Use the kivy garden directory (kivy/garden)') p.set_defaults(func=self.cmd_list) p = subparsers.add_parser('search', help='Search garden package on github') p.add_argument('pattern', nargs='?', default='', help='Word to search in the package name (optional)') p.set_defaults(func=self.cmd_search) p = subparsers.add_parser('install', help='Install a garden package') p.add_argument('--app', action='store_true', help='Install in the local app directory (./libs/garden)') p.add_argument('--kivy', action='store_true', help='Use the kivy garden directory (kivy/garden)') p.add_argument('--upgrade', action='store_true', help='Force the installation') p.add_argument('--no-download-progress', action='store_false', dest='animate', help='Disable download progress indicator') p.add_argument('package', nargs=1, help='Name of the package to install') p.set_defaults(func=self.cmd_install) p = subparsers.add_parser('uninstall', help='Uninstall a garden package') p.add_argument('--app', action='store_true', help='Use the local app directory (./libs/garden)') p.add_argument('--kivy', action='store_true', help='Use the kivy garden directory (kivy/garden)') p.add_argument('package', nargs=1, help='Name of the package to uninstall') p.set_defaults(func=self.cmd_uninstall) self.options = options = parser.parse_args(argv) options.package = [p.lower() for p in getattr(options, 'package', ())] if hasattr(options, 'func'): options.func() # No cmd supplied, print help message else: parser.print_help() if getattr(self.options, 'kivy', False) and garden_kivy_dir is None: print('--kivy provided; cannot find kivy') sys.exit(0) def cmd_list(self): if self.options.kivy: for filename in listdir(garden_kivy_dir): fullname = join(garden_kivy_dir, filename) if isdir(fullname): print(fullname) return directory = garden_app_dir if self.options.app else garden_system_dir if not exists(directory): return for filename in listdir(directory): fullname = join(directory, filename) if filename.startswith('garden.') and isdir(fullname): print(filename.split('.', 1)[-1]) def cmd_search(self): r = requests.get('https://api.github.com/users/kivy-garden/repos') pattern = self.options.pattern data = r.json() for repo in data: if not repo['name'].startswith('garden.'): continue name = repo['name'].split('.', 1)[-1] if pattern and pattern not in name: continue desc = repo['description'] print("{} - {}".format( name, desc.splitlines()[0] if desc else 'No description')) def cmd_install(self): opts = self.options src_package = self.gardenify(opts.package[0]) dst_package = opts.package[0] if self.options.kivy else src_package garden_dir = garden_kivy_dir if self.options.kivy else ( garden_app_dir if self.options.app else garden_system_dir) dest_dir = join(garden_dir, dst_package) if exists(dest_dir) and not opts.upgrade: print('Garden package already installed in {}'.format(dest_dir)) print('Use --upgrade to upgrade.') sys.exit(0) fd = self.download(src_package, opts.animate) tempdir = tempfile.mkdtemp(prefix='garden-') try: self.extract(fd, tempdir) if not exists(garden_dir): makedirs(garden_dir) if exists(dest_dir): print('Removing old version...') rmtree(dest_dir) source_directory = join(tempdir, '{}-master'.format(src_package)) print('Installing new version...') move(source_directory, dest_dir) print('Done! {} is installed at: {}'.format(src_package, dest_dir)) finally: print('Cleaning...') if exists(tempdir): rmtree(tempdir, ignore_errors=True) def cmd_uninstall(self): opts = self.options package = opts.package[0] if self.options.kivy else \ self.gardenify(opts.package[0]) garden_dir = garden_kivy_dir if self.options.kivy else ( garden_app_dir if self.options.app else garden_system_dir) d = join(garden_dir, package) if not exists(d): print('Package {} not installed, nothing to uninstall.'.format( package)) sys.exit(0) print('Deleting {}...'.format(d)) rmtree(d) def gardenify(self, package): if not package.startswith('garden.'): return 'garden.' + package return package def download(self, package, animate): url = 'https://github.com/kivy-garden/{}/archive/master.zip'.format( package) print('Downloading {} ...'.format(url)) r = requests.get(url)#, prefetch=False) if r.status_code != 200: print('Unable to find the garden package. (error={})'.format( r.status_code)) sys.exit(1) animation = '\\|/-' index = 0 count = 0 data = b'' for buf in r.iter_content(1024): index += 1 data += buf count += len(buf) if animate: print('Progression', count, animation[index % len(animation)], '\r') sys.stdout.flush() print('Download done ({} downloaded)'.format(count)) return BytesIO(data) def extract(self, fd, directory): print('Extracting...') z = zipfile.ZipFile(fd) curdir = getcwd() chdir(directory) z.extractall() chdir(curdir) if __name__ == '__main__': GardenTool().main(sys.argv[1:])