260 lines
7.9 KiB
Python
260 lines
7.9 KiB
Python
|
#!/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))
|