angrybeanie_wagtail/env/lib/python3.12/site-packages/wagtail/bin/wagtail.py

458 lines
14 KiB
Python
Raw Normal View History

2025-07-25 21:32:16 +10:00
#!/usr/bin/env python
import fileinput
import fnmatch
import os
import re
import sys
from argparse import ArgumentParser
from difflib import unified_diff
from django.core.management import ManagementUtility
CURRENT_PYTHON = sys.version_info[:2]
REQUIRED_PYTHON = (3, 7)
if CURRENT_PYTHON < REQUIRED_PYTHON:
sys.stderr.write(
"This version of Wagtail requires Python {}.{} or above - you are running {}.{}\n".format(
*(REQUIRED_PYTHON + CURRENT_PYTHON)
)
)
sys.exit(1)
def pluralize(value, arg="s"):
return "" if value == 1 else arg
class Command:
description = None
def create_parser(self, command_name=None):
if command_name is None:
prog = None
else:
# hack the prog name as reported to ArgumentParser to include the command
prog = f"{prog_name()} {command_name}"
parser = ArgumentParser(
description=getattr(self, "description", None), add_help=False, prog=prog
)
self.add_arguments(parser)
return parser
def add_arguments(self, parser):
pass
def print_help(self, command_name):
parser = self.create_parser(command_name=command_name)
parser.print_help()
def execute(self, argv):
parser = self.create_parser()
options = parser.parse_args(sys.argv[2:])
options_dict = vars(options)
self.run(**options_dict)
class CreateProject(Command):
description = "Creates the directory structure for a new Wagtail project."
def __init__(self):
self.default_template_path = self.get_default_template_path()
def add_arguments(self, parser):
parser.add_argument("project_name", help="Name for your Wagtail project")
parser.add_argument(
"dest_dir",
nargs="?",
help="Destination directory inside which to create the project",
)
parser.add_argument(
"--template",
help="The path or URL to load the template from.",
default=self.default_template_path,
)
def get_default_template_path(self):
import wagtail
wagtail_path = os.path.dirname(wagtail.__file__)
default_template_path = os.path.join(wagtail_path, "project_template")
return default_template_path
def run(self, project_name=None, dest_dir=None, **options):
# Make sure given name is not already in use by another python package/module.
try:
__import__(project_name)
except ImportError:
pass
else:
sys.exit(
"'%s' conflicts with the name of an existing "
"Python module and cannot be used as a project "
"name. Please try another name." % project_name
)
template_name = options["template"]
if template_name == self.default_template_path:
template_name = "the default Wagtail template"
print( # noqa: T201
"Creating a Wagtail project called %(project_name)s using %(template_name)s"
% {"project_name": project_name, "template_name": template_name}
)
# Call django-admin startproject
utility_args = [
"django-admin",
"startproject",
"--template=" + options["template"],
"--ext=html,rst",
"--name=Dockerfile",
project_name,
]
if dest_dir:
utility_args.append(dest_dir)
utility = ManagementUtility(utility_args)
utility.execute()
print( # noqa: T201
"Success! %(project_name)s has been created"
% {"project_name": project_name}
)
class UpdateModulePaths(Command):
description = "Update a Wagtail project tree to use Wagtail 2.x module paths"
REPLACEMENTS = [
# Added in Wagtail 2.0
(re.compile(r"\bwagtail\.wagtailcore\b"), "wagtail"),
(re.compile(r"\bwagtail\.wagtailadmin\b"), "wagtail.admin"),
(re.compile(r"\bwagtail\.wagtaildocs\b"), "wagtail.documents"),
(re.compile(r"\bwagtail\.wagtailembeds\b"), "wagtail.embeds"),
(re.compile(r"\bwagtail\.wagtailimages\b"), "wagtail.images"),
(re.compile(r"\bwagtail\.wagtailsearch\b"), "wagtail.search"),
(re.compile(r"\bwagtail\.wagtailsites\b"), "wagtail.sites"),
(re.compile(r"\bwagtail\.wagtailsnippets\b"), "wagtail.snippets"),
(re.compile(r"\bwagtail\.wagtailusers\b"), "wagtail.users"),
(re.compile(r"\bwagtail\.wagtailforms\b"), "wagtail.contrib.forms"),
(re.compile(r"\bwagtail\.wagtailredirects\b"), "wagtail.contrib.redirects"),
(
re.compile(r"\bwagtail\.contrib\.wagtailfrontendcache\b"),
"wagtail.contrib.frontend_cache",
),
(
re.compile(r"\bwagtail\.contrib\.wagtailroutablepage\b"),
"wagtail.contrib.routable_page",
),
(
re.compile(r"\bwagtail\.contrib\.wagtailsearchpromotions\b"),
"wagtail.contrib.search_promotions",
),
(
re.compile(r"\bwagtail\.contrib\.wagtailsitemaps\b"),
"wagtail.contrib.sitemaps",
),
(
re.compile(r"\bwagtail\.contrib\.wagtailstyleguide\b"),
"wagtail.contrib.styleguide",
),
# Added in Wagtail 3.0
(re.compile(r"\bwagtail\.tests\b"), "wagtail.test"),
(re.compile(r"\bwagtail\.core\.utils\b"), "wagtail.coreutils"),
(re.compile(r"\bwagtail\.core\b"), "wagtail"),
(re.compile(r"\bwagtail\.admin\.edit_handlers\b"), "wagtail.admin.panels"),
(
re.compile(r"\bwagtail\.contrib\.forms\.edit_handlers\b"),
"wagtail.contrib.forms.panels",
),
]
def add_arguments(self, parser):
parser.add_argument("root_path", nargs="?", help="Path to your project's root")
parser.add_argument(
"--list",
action="store_true",
dest="list_files",
help="Show the list of files to change, without modifying them",
)
parser.add_argument(
"--diff",
action="store_true",
help="Show the changes that would be made, without modifying the files",
)
parser.add_argument(
"--ignore-dir",
action="append",
dest="ignored_dirs",
metavar="NAME",
help="Ignore files in this directory",
)
parser.add_argument(
"--ignore-file",
action="append",
dest="ignored_patterns",
metavar="NAME",
help="Ignore files with this name (supports wildcards)",
)
def run(
self,
root_path=None,
list_files=False,
diff=False,
ignored_dirs=None,
ignored_patterns=None,
):
if root_path is None:
root_path = os.getcwd()
absolute_ignored_dirs = [
os.path.abspath(dir_path) + os.sep for dir_path in (ignored_dirs or [])
]
if ignored_patterns is None:
ignored_patterns = []
checked_file_count = 0
changed_file_count = 0
for dirpath, dirnames, filenames in os.walk(root_path):
dirpath_with_slash = os.path.abspath(dirpath) + os.sep
if any(
dirpath_with_slash.startswith(ignored_dir)
for ignored_dir in absolute_ignored_dirs
):
continue
for filename in filenames:
if not filename.lower().endswith(".py"):
continue
if any(
fnmatch.fnmatch(filename, pattern) for pattern in ignored_patterns
):
continue
path = os.path.join(dirpath, filename)
relative_path = os.path.relpath(path, start=root_path)
checked_file_count += 1
if diff:
change_count = self._show_diff(path, relative_path=relative_path)
else:
if list_files:
change_count = self._count_changes(path)
else: # actually update
change_count = self._rewrite_file(path)
if change_count:
print( # noqa: T201
"%s - %d change%s"
% (relative_path, change_count, pluralize(change_count))
)
if change_count:
changed_file_count += 1
if diff or list_files:
print( # noqa: T201
"\nChecked %d .py file%s, %d file%s to update."
% (
checked_file_count,
pluralize(checked_file_count),
changed_file_count,
pluralize(changed_file_count),
)
)
else:
print( # noqa: T201
"\nChecked %d .py file%s, %d file%s updated."
% (
checked_file_count,
pluralize(checked_file_count),
changed_file_count,
pluralize(changed_file_count),
)
)
def _rewrite_line(self, line):
for pattern, repl in self.REPLACEMENTS:
line = re.sub(pattern, repl, line)
return line
def _show_diff(self, filename, relative_path=None):
change_count = 0
found_unicode_error = False
original = []
updated = []
with open(filename, mode="rb") as f:
for raw_original_line in f:
try:
original_line = raw_original_line.decode("utf-8")
except UnicodeDecodeError:
found_unicode_error = True
# retry decoding as utf-8, mangling invalid bytes so that we have a usable string to use the diff
line = original_line = raw_original_line.decode(
"utf-8", errors="replace"
)
else:
line = self._rewrite_line(original_line)
original.append(original_line)
updated.append(line)
if line != original_line:
change_count += 1
if found_unicode_error:
sys.stderr.write(
"Warning - %s is not a valid UTF-8 file. Lines with decode errors have been ignored\n"
% filename
)
if change_count:
relative_path = relative_path or filename
sys.stdout.writelines(
unified_diff(
original,
updated,
fromfile="%s:before" % relative_path,
tofile="%s:after" % relative_path,
)
)
return change_count
def _count_changes(self, filename):
change_count = 0
found_unicode_error = False
with open(filename, mode="rb") as f:
for raw_original_line in f:
try:
original_line = raw_original_line.decode("utf-8")
except UnicodeDecodeError:
found_unicode_error = True
else:
line = self._rewrite_line(original_line)
if line != original_line:
change_count += 1
if found_unicode_error:
sys.stderr.write(
"Warning - %s is not a valid UTF-8 file. Lines with decode errors have been ignored\n"
% filename
)
return change_count
def _rewrite_file(self, filename):
change_count = 0
found_unicode_error = False
with fileinput.FileInput(filename, inplace=True, mode="rb") as f:
for raw_original_line in f:
try:
original_line = raw_original_line.decode("utf-8")
except UnicodeDecodeError:
sys.stdout.write(raw_original_line)
found_unicode_error = True
else:
line = self._rewrite_line(original_line)
if CURRENT_PYTHON >= (3, 8):
sys.stdout.write(line.encode("utf-8"))
else:
# Python 3.7 opens the output stream in text mode, so write the line back as
# text rather than bytes:
# https://github.com/python/cpython/commit/be6dbfb43b89989ccc83fbc4c5234f50f44c47ad
sys.stdout.write(line)
if line != original_line:
change_count += 1
if found_unicode_error:
sys.stderr.write(
"Warning - %s is not a valid UTF-8 file. Lines with decode errors have been ignored\n"
% filename
)
return change_count
class Version(Command):
description = "List which version of Wagtail you are using"
def run(self):
import wagtail
version = wagtail.get_version(wagtail.VERSION)
print(f"You are using Wagtail {version}") # noqa: T201
COMMANDS = {
"start": CreateProject(),
"updatemodulepaths": UpdateModulePaths(),
"--version": Version(),
}
def prog_name():
return os.path.basename(sys.argv[0])
def help_index():
print( # noqa: T201
"Type '%s help <subcommand>' for help on a specific subcommand.\n" % prog_name()
)
print("Available subcommands:\n") # NOQA: T201
for name, cmd in sorted(COMMANDS.items()):
print(f" {name.ljust(20)}{cmd.description}") # NOQA: T201
def unknown_command(command):
print("Unknown command: '%s'" % command) # NOQA: T201
print("Type '%s help' for usage." % prog_name()) # NOQA: T201
sys.exit(1)
def main():
try:
command_name = sys.argv[1]
except IndexError:
help_index()
return
if command_name == "help":
try:
help_command_name = sys.argv[2]
except IndexError:
help_index()
return
try:
command = COMMANDS[help_command_name]
except KeyError:
unknown_command(help_command_name)
return
command.print_help(help_command_name)
return
try:
command = COMMANDS[command_name]
except KeyError:
unknown_command(command_name)
return
command.execute(sys.argv)
if __name__ == "__main__":
main()