#!/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 isinstance(die, 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 theme_path, link_target in themes(): if not v: theme_path = os.path.basename(theme_path) if link_target: if v: print(theme_path + (" (symbolic link to `" + link_target + "')")) else: print(theme_path + "@") else: print(theme_path) 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: 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) and 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))