#!/usr/bin/env python import argparse import os import shutil import sys def err(msg, die=None): """Print an error message and exits if an exit code is given""" sys.stderr.write(msg + '\n') if die: sys.exit(die if type(die) is int else 1) try: import pelican except ImportError: err('Cannot import pelican.\nYou must ' 'install Pelican in order to run this script.', -1) global _THEMES_PATH _THEMES_PATH = os.path.join( os.path.dirname( os.path.abspath(pelican.__file__) ), 'themes' ) __version__ = '0.2' _BUILTIN_THEMES = ['simple', 'notmyidea'] def main(): """Main function""" parser = argparse.ArgumentParser( description="""Install themes for Pelican""") excl = parser.add_mutually_exclusive_group() excl.add_argument( '-l', '--list', dest='action', action="store_const", const='list', help="Show the themes already installed and exit") excl.add_argument( '-p', '--path', dest='action', action="store_const", const='path', help="Show the themes path and exit") excl.add_argument( '-V', '--version', action='version', version='pelican-themes v{}'.format(__version__), help='Print the version of this script') parser.add_argument( '-i', '--install', dest='to_install', nargs='+', metavar="theme path", help='The themes to install') parser.add_argument( '-r', '--remove', dest='to_remove', nargs='+', metavar="theme name", help='The themes to remove') parser.add_argument( '-U', '--upgrade', dest='to_upgrade', nargs='+', metavar="theme path", help='The themes to upgrade') parser.add_argument( '-s', '--symlink', dest='to_symlink', nargs='+', metavar="theme path", help="Same as `--install', but create a symbolic link instead of " "copying the theme. Useful for theme development") parser.add_argument( '-c', '--clean', dest='clean', action="store_true", help="Remove the broken symbolic links of the theme path") parser.add_argument( '-v', '--verbose', dest='verbose', action="store_true", help="Verbose output") args = parser.parse_args() to_install = args.to_install or args.to_upgrade to_sym = args.to_symlink or args.clean if args.action: if args.action == 'list': list_themes(args.verbose) elif args.action == 'path': print(_THEMES_PATH) elif to_install or args.to_remove or to_sym: if args.to_remove: if args.verbose: print('Removing themes...') for i in args.to_remove: remove(i, v=args.verbose) if args.to_install: if args.verbose: print('Installing themes...') for i in args.to_install: install(i, v=args.verbose) if args.to_upgrade: if args.verbose: print('Upgrading themes...') for i in args.to_upgrade: install(i, v=args.verbose, u=True) if args.to_symlink: if args.verbose: print('Linking themes...') for i in args.to_symlink: symlink(i, v=args.verbose) if args.clean: if args.verbose: print('Cleaning the themes directory...') clean(v=args.verbose) else: print('No argument given... exiting.') def themes(): """Returns the list of the themes""" for i in os.listdir(_THEMES_PATH): e = os.path.join(_THEMES_PATH, i) if os.path.isdir(e): if os.path.islink(e): yield (e, os.readlink(e)) else: yield (e, None) def list_themes(v=False): """Display the list of the themes""" for t, l in themes(): if not v: t = os.path.basename(t) if l: if v: print(t + (" (symbolic link to `" + l + "')")) else: print(t + '@') else: print(t) def remove(theme_name, v=False): """Removes a theme""" theme_name = theme_name.replace('/', '') target = os.path.join(_THEMES_PATH, theme_name) if theme_name in _BUILTIN_THEMES: err(theme_name + ' is a builtin theme.\n' 'You cannot remove a builtin theme with this script, ' 'remove it by hand if you want.') elif os.path.islink(target): if v: print('Removing link `' + target + "'") os.remove(target) elif os.path.isdir(target): if v: print('Removing directory `' + target + "'") shutil.rmtree(target) elif os.path.exists(target): err(target + ' : not a valid theme') else: err(target + ' : no such file or directory') def install(path, v=False, u=False): """Installs a theme""" if not os.path.exists(path): err(path + ' : no such file or directory') elif not os.path.isdir(path): err(path + ' : not a directory') else: theme_name = os.path.basename(os.path.normpath(path)) theme_path = os.path.join(_THEMES_PATH, theme_name) exists = os.path.exists(theme_path) if exists and not u: err(path + ' : already exists') elif exists and u: remove(theme_name, v) install(path, v) else: if v: print("Copying '{p}' to '{t}' ...".format(p=path, t=theme_path)) try: shutil.copytree(path, theme_path) try: if os.name == 'posix': for root, dirs, files in os.walk(theme_path): for d in dirs: dname = os.path.join(root, d) os.chmod(dname, 493) # 0o755 for f in files: fname = os.path.join(root, f) os.chmod(fname, 420) # 0o644 except OSError as e: err("Cannot change permissions of files " "or directory in `{r}':\n{e}".format(r=theme_path, e=str(e)), die=False) except Exception as e: err("Cannot copy `{p}' to `{t}':\n{e}".format( p=path, t=theme_path, e=str(e))) def symlink(path, v=False): """Symbolically link a theme""" if not os.path.exists(path): err(path + ' : no such file or directory') elif not os.path.isdir(path): err(path + ' : not a directory') else: theme_name = os.path.basename(os.path.normpath(path)) theme_path = os.path.join(_THEMES_PATH, theme_name) if os.path.exists(theme_path): err(path + ' : already exists') else: if v: print("Linking `{p}' to `{t}' ...".format( p=path, t=theme_path)) try: os.symlink(path, theme_path) except Exception as e: err("Cannot link `{p}' to `{t}':\n{e}".format( p=path, t=theme_path, e=str(e))) def is_broken_link(path): """Returns True if the path given as is a broken symlink""" path = os.readlink(path) return not os.path.exists(path) def clean(v=False): """Removes the broken symbolic links""" c = 0 for path in os.listdir(_THEMES_PATH): path = os.path.join(_THEMES_PATH, path) if os.path.islink(path): if is_broken_link(path): if v: print('Removing {}'.format(path)) try: os.remove(path) except OSError: print('Error: cannot remove {}'.format(path)) else: c += 1 print("\nRemoved {} broken links".format(c))