pelican/pelican/tools/pelican_themes.py
2023-10-29 22:18:29 +01:00

299 lines
8.3 KiB
Python
Executable file

#!/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))