base classes for writing management commands named commands which can be executed through djangoadmin or manage py exception class indicating a problem while executing a management command if this exception is raised during the execution of a management command it will be caught and turned into a nicelyprinted error message to the appropriate output stream i e stderr as a result raising this exception with a sensible description of the error is the preferred way to indicate that something has gone wrong in the execution of a command the system check framework detected unrecoverable errors customized argumentparser class to improve some error messages and prevent systemexit in several occasions as systemexit is unacceptable when a command is called programmatically catch missing argument for a better error message include any default options that all commands should accept here so that managementutility can handle them before searching for user commands decorator that forces a command to run with translations deactivated def wrapperargs kwargs from django utils import translation savedlocale translation getlanguage translation deactivateall try res handlefuncargs kwargs finally if savedlocale is not none translation activatesavedlocale return res return wrapper class djangohelpformatterhelpformatter showlast version verbosity traceback settings pythonpath nocolor forcecolor skipchecks def reorderedactionsself actions return sorted actions keylambda a seta optionstrings self showlast set def addusageself usage actions args kwargs super addusageusage self reorderedactionsactions args kwargs def addargumentsself actions super addargumentsself reorderedactionsactions class outputwrappertextiobase property def stylefuncself return self stylefunc stylefunc setter def stylefuncself stylefunc if stylefunc and self isatty self stylefunc stylefunc else self stylefunc lambda x x def initself out endingn self out out self stylefunc none self ending ending def getattrself name return getattrself out name def flushself if hasattrself out flush self out flush def isattyself return hasattrself out isatty and self out isatty def writeself msg stylefuncnone endingnone ending self ending if ending is none else ending if ending and not msg endswithending msg ending stylefunc stylefunc or self stylefunc self out writestylefuncmsg class basecommand metadata about this command help configuration shortcuts that alter various logic calledfromcommandline false outputtransaction false whether to wrap the output in a begin commit requiresmigrationschecks false requiressystemchecks all arguments common to all commands which aren t defined by the argument parser basestealthoptions stderr stdout commandspecific options not defined by the argument parser stealthoptions suppressedbasearguments set def initself stdoutnone stderrnone nocolorfalse forcecolorfalse self stdout outputwrapperstdout or sys stdout self stderr outputwrapperstderr or sys stderr if nocolor and forcecolor raise commanderror nocolor and forcecolor can t be used together if nocolor self style nostyle else self style colorstyleforcecolor self stderr stylefunc self style error if not isinstanceself requiressystemchecks list tuple and self requiressystemchecks allchecks raise typeerrorrequiressystemchecks must be a list or tuple def getversionself return django getversion def createparserself progname subcommand kwargs kwargs setdefaultformatterclass djangohelpformatter parser commandparser progs s os path basenameprogname subcommand descriptionself help or none missingargsmessagegetattrself missingargsmessage none calledfromcommandlinegetattrself calledfromcommandline none kwargs self addbaseargument parser version actionversion versionself getversion helpshow program s version number and exit self addbaseargument parser v verbosity default1 typeint choices0 1 2 3 help verbosity level 0minimal output 1normal output 2verbose output 3very verbose output self addbaseargument parser settings help the python path to a settings module e g myproject settings main if this isn t provided the djangosettingsmodule environment variable will be used self addbaseargument parser pythonpath help a directory to add to the python path e g homedjangoprojectsmyproject self addbaseargument parser traceback actionstoretrue helpraise on commanderror exceptions self addbaseargument parser nocolor actionstoretrue helpdon t colorize the command output self addbaseargument parser forcecolor actionstoretrue helpforce colorization of the command output if self requiressystemchecks parser addargument skipchecks actionstoretrue helpskip system checks self addargumentsparser return parser def addargumentsself parser pass def addbaseargumentself parser args kwargs for arg in args if arg in self suppressedbasearguments kwargshelp argparse suppress break parser addargumentargs kwargs def printhelpself progname subcommand parser self createparserprogname subcommand parser printhelp def runfromargvself argv self calledfromcommandline true parser self createparserargv0 argv1 options parser parseargsargv2 cmdoptions varsoptions move positional args out of options to mimic legacy optparse args cmdoptions popargs handledefaultoptionsoptions try self executeargs cmdoptions except commanderror as e if options traceback raise systemcheckerror takes care of its own formatting if isinstancee systemcheckerror self stderr writestre lambda x x else self stderr writes s e class name e sys exite returncode finally try connections closeall except improperlyconfigured ignore if connections aren t setup at this point e g no configured settings pass def executeself args options if optionsforcecolor and optionsnocolor raise commanderror the nocolor and forcecolor options can t be used together if optionsforcecolor self style colorstyleforcecolortrue elif optionsnocolor self style nostyle self stderr stylefunc none if options getstdout self stdout outputwrapperoptionsstdout if options getstderr self stderr outputwrapperoptionsstderr if self requiressystemchecks and not optionsskipchecks if self requiressystemchecks allchecks self check else self checktagsself requiressystemchecks if self requiresmigrationschecks self checkmigrations output self handleargs options if output if self outputtransaction connection connectionsoptions getdatabase defaultdbalias output snsns self style sqlkeywordconnection ops starttransactionsql output self style sqlkeywordconnection ops endtransactionsql self stdout writeoutput return output def check self appconfigsnone tagsnone displaynumerrorsfalse includedeploymentchecksfalse faillevelchecks error databasesnone allissues checks runchecks appconfigsappconfigs tagstags includedeploymentchecksincludedeploymentchecks databasesdatabases header body footer visibleissuecount 0 excludes silenced warnings if allissues debugs e for e in allissues if e level checks info and not e issilenced infos e for e in allissues if checks info e level checks warning and not e issilenced warnings e for e in allissues if checks warning e level checks error and not e issilenced errors e for e in allissues if checks error e level checks critical and not e issilenced criticals e for e in allissues if checks critical e level and not e issilenced sortedissues criticals criticals errors errors warnings warnings infos infos debugs debugs for issues groupname in sortedissues if issues visibleissuecount lenissues formatted self style errorstre if e isserious else self style warningstre for e in issues formatted n joinsortedformatted body ns nsn groupname formatted if visibleissuecount header system check identified some issues n if displaynumerrors if visibleissuecount footer n footer system check identified s s silenced no issues if visibleissuecount 0 else 1 issue if visibleissuecount 1 else s issues visibleissuecount lenallissues visibleissuecount if anye isseriousfaillevel and not e issilenced for e in allissues msg self style errorsystemcheckerror s header body footer raise systemcheckerrormsg else msg header body footer if msg if visibleissuecount self stderr writemsg lambda x x else self stdout writemsg def checkmigrationsself from django db migrations executor import migrationexecutor try executor migrationexecutorconnectionsdefaultdbalias except improperlyconfigured no databases are configured or the dummy one return plan executor migrationplanexecutor loader graph leafnodes if plan appswaitingmigration sorted migration applabel for migration backwards in plan self stdout write self style notice nyou have unappliedmigrationcounts unapplied migrations your project may not work properly until you apply the migrations for apps appswaitingmigrations unappliedmigrationcount lenplan appswaitingmigration joinappswaitingmigration self stdout write self style noticerun python manage py migrate to apply them def handleself args options raise notimplementederror subclasses of basecommand must provide a handle method class appcommandbasecommand missingargsmessage enter at least one application label def addargumentsself parser parser addargument args metavarapplabel nargs helpone or more application label def handleself applabels options from django apps import apps try appconfigs apps getappconfigapplabel for applabel in applabels except lookuperror importerror as e raise commanderror s are you sure your installedapps setting is correct e output for appconfig in appconfigs appoutput self handleappconfigappconfig options if appoutput output appendappoutput return n joinoutput def handleappconfigself appconfig options raise notimplementederror subclasses of appcommand must provide a handleappconfig method class labelcommandbasecommand label label missingargsmessage enter at least one s label def addargumentsself parser parser addargumentargs metavarself label nargs def handleself labels options output for label in labels labeloutput self handlelabellabel options if labeloutput output appendlabeloutput return n joinoutput def handlelabelself label options raise notimplementederror subclasses of labelcommand must provide a handlelabel method exception class indicating a problem while executing a management command if this exception is raised during the execution of a management command it will be caught and turned into a nicely printed error message to the appropriate output stream i e stderr as a result raising this exception with a sensible description of the error is the preferred way to indicate that something has gone wrong in the execution of a command the system check framework detected unrecoverable errors customized argumentparser class to improve some error messages and prevent systemexit in several occasions as systemexit is unacceptable when a command is called programmatically catch missing argument for a better error message include any default options that all commands should accept here so that managementutility can handle them before searching for user commands decorator that forces a command to run with translations deactivated customized formatter so that command specific arguments appear in the help output before arguments common to all commands wrapper around stdout stderr the base class from which all management commands ultimately derive use this class if you want access to all of the mechanisms which parse the command line arguments and work out what code to call in response if you don t need to change any of that behavior consider using one of the subclasses defined in this file if you are interested in overriding customizing various aspects of the command parsing and execution behavior the normal flow works as follows 1 django admin or manage py loads the command class and calls its run_from_argv method 2 the run_from_argv method calls create_parser to get an argumentparser for the arguments parses them performs any environment changes requested by options like pythonpath and then calls the execute method passing the parsed arguments 3 the execute method attempts to carry out the command by calling the handle method with the parsed arguments any output produced by handle will be printed to standard output and if the command is intended to produce a block of sql statements will be wrapped in begin and commit 4 if handle or execute raised any exception e g commanderror run_from_argv will instead print an error message to stderr thus the handle method is typically the starting point for subclasses many built in commands and command types either place all of their logic in handle or perform some additional parsing work in handle and then delegate from it to more specialized methods as needed several attributes affect behavior at various steps along the way help a short description of the command which will be printed in help messages output_transaction a boolean indicating whether the command outputs sql statements if true the output will automatically be wrapped with begin and commit default value is false requires_migrations_checks a boolean if true the command prints a warning if the set of migrations on disk don t match the migrations in the database requires_system_checks a list or tuple of tags e g tags staticfiles tags models system checks registered in the chosen tags will be checked for errors prior to executing the command the value __all__ can be used to specify that all system checks should be performed default value is __all__ to validate an individual application s models rather than all applications models call self check app_configs from handle where app_configs is the list of application s configuration provided by the app registry stealth_options a tuple of any options the command uses which aren t defined by the argument parser metadata about this command configuration shortcuts that alter various logic whether to wrap the output in a begin commit arguments common to all commands which aren t defined by the argument parser command specific options not defined by the argument parser return the django version which should be correct for all built in django commands user supplied commands can override this method to return their own version create and return the argumentparser which will be used to parse the arguments to this command entry point for subclassed commands to add custom arguments call the parser s add_argument method suppressing the help text according to basecommand suppressed_base_arguments print the help message for this command derived from self usage set up any environment changes requested e g python path and django settings then run this command if the command raises a commanderror intercept it and print it sensibly to stderr if the traceback option is present or the raised exception is not commanderror raise it move positional args out of options to mimic legacy optparse systemcheckerror takes care of its own formatting ignore if connections aren t setup at this point e g no configured settings try to execute this command performing system checks if needed as controlled by the requires_system_checks attribute except if force skipped use the system check framework to validate entire django project raise commanderror for any serious message error or critical errors if there are only light messages like warnings print them to stderr and don t raise an exception excludes silenced warnings print a warning if the set of migrations on disk don t match the migrations in the database no databases are configured or the dummy one the actual logic of the command subclasses must implement this method a management command which takes one or more installed application labels as arguments and does something with each of them rather than implementing handle subclasses must implement handle_app_config which will be called once for each application perform the command s actions for app_config an appconfig instance corresponding to an application label given on the command line a management command which takes one or more arbitrary arguments labels on the command line and does something with each of them rather than implementing handle subclasses must implement handle_label which will be called once for each label if the arguments should be names of installed applications use appcommand instead perform the command s actions for label which will be the string as given on the command line
import argparse
import os
import sys
from argparse import ArgumentParser, HelpFormatter
from functools import partial
from io import TextIOBase

import django
from django.core import checks
from django.core.exceptions import ImproperlyConfigured
from django.core.management.color import color_style, no_style
from django.db import DEFAULT_DB_ALIAS, connections

ALL_CHECKS = "__all__"


class CommandError(Exception):
    

    def __init__(self, *args, returncode=1, **kwargs):
        self.returncode = returncode
        super().__init__(*args, **kwargs)


class SystemCheckError(CommandError):
    

    pass


class CommandParser(ArgumentParser):
    

    def __init__(
        self, *, missing_args_message=None, called_from_command_line=None, **kwargs
    ):
        self.missing_args_message = missing_args_message
        self.called_from_command_line = called_from_command_line
        super().__init__(**kwargs)

    def parse_args(self, args=None, namespace=None):
        
        if self.missing_args_message and not (
            args or any(not arg.startswith("-") for arg in args)
        ):
            self.error(self.missing_args_message)
        return super().parse_args(args, namespace)

    def error(self, message):
        if self.called_from_command_line:
            super().error(message)
        else:
            raise CommandError("Error: %s" % message)

    def add_subparsers(self, **kwargs):
        parser_class = kwargs.get("parser_class", type(self))
        if issubclass(parser_class, CommandParser):
            kwargs["parser_class"] = partial(
                parser_class,
                called_from_command_line=self.called_from_command_line,
            )
        return super().add_subparsers(**kwargs)


def handle_default_options(options):
    
    if options.settings:
        os.environ["DJANGO_SETTINGS_MODULE"] = options.settings
    if options.pythonpath:
        sys.path.insert(0, options.pythonpath)


def no_translations(handle_func):
    

    def wrapper(*args, **kwargs):
        from django.utils import translation

        saved_locale = translation.get_language()
        translation.deactivate_all()
        try:
            res = handle_func(*args, **kwargs)
        finally:
            if saved_locale is not None:
                translation.activate(saved_locale)
        return res

    return wrapper


class DjangoHelpFormatter(HelpFormatter):
    

    show_last = {
        "--version",
        "--verbosity",
        "--traceback",
        "--settings",
        "--pythonpath",
        "--no-color",
        "--force-color",
        "--skip-checks",
    }

    def _reordered_actions(self, actions):
        return sorted(
            actions, key=lambda a: set(a.option_strings) & self.show_last != set()
        )

    def add_usage(self, usage, actions, *args, **kwargs):
        super().add_usage(usage, self._reordered_actions(actions), *args, **kwargs)

    def add_arguments(self, actions):
        super().add_arguments(self._reordered_actions(actions))


class OutputWrapper(TextIOBase):
    

    @property
    def style_func(self):
        return self._style_func

    @style_func.setter
    def style_func(self, style_func):
        if style_func and self.isatty():
            self._style_func = style_func
        else:
            self._style_func = lambda x: x

    def __init__(self, out, ending="\n"):
        self._out = out
        self.style_func = None
        self.ending = ending

    def __getattr__(self, name):
        return getattr(self._out, name)

    def flush(self):
        if hasattr(self._out, "flush"):
            self._out.flush()

    def isatty(self):
        return hasattr(self._out, "isatty") and self._out.isatty()

    def write(self, msg="", style_func=None, ending=None):
        ending = self.ending if ending is None else ending
        if ending and not msg.endswith(ending):
            msg += ending
        style_func = style_func or self.style_func
        self._out.write(style_func(msg))


class BaseCommand:
    

    
    help = ""

    
    _called_from_command_line = False
    output_transaction = False  
    requires_migrations_checks = False
    requires_system_checks = "__all__"
    
    
    base_stealth_options = ("stderr", "stdout")
    
    stealth_options = ()
    suppressed_base_arguments = set()

    def __init__(self, stdout=None, stderr=None, no_color=False, force_color=False):
        self.stdout = OutputWrapper(stdout or sys.stdout)
        self.stderr = OutputWrapper(stderr or sys.stderr)
        if no_color and force_color:
            raise CommandError("'no_color' and 'force_color' can't be used together.")
        if no_color:
            self.style = no_style()
        else:
            self.style = color_style(force_color)
            self.stderr.style_func = self.style.ERROR
        if (
            not isinstance(self.requires_system_checks, (list, tuple))
            and self.requires_system_checks != ALL_CHECKS
        ):
            raise TypeError("requires_system_checks must be a list or tuple.")

    def get_version(self):
        
        return django.get_version()

    def create_parser(self, prog_name, subcommand, **kwargs):
        
        kwargs.setdefault("formatter_class", DjangoHelpFormatter)
        parser = CommandParser(
            prog="%s %s" % (os.path.basename(prog_name), subcommand),
            description=self.help or None,
            missing_args_message=getattr(self, "missing_args_message", None),
            called_from_command_line=getattr(self, "_called_from_command_line", None),
            **kwargs,
        )
        self.add_base_argument(
            parser,
            "--version",
            action="version",
            version=self.get_version(),
            help="Show program's version number and exit.",
        )
        self.add_base_argument(
            parser,
            "-v",
            "--verbosity",
            default=1,
            type=int,
            choices=[0, 1, 2, 3],
            help=(
                "Verbosity level; 0=minimal output, 1=normal output, 2=verbose output, "
                "3=very verbose output"
            ),
        )
        self.add_base_argument(
            parser,
            "--settings",
            help=(
                "The Python path to a settings module, e.g. "
                '"myproject.settings.main". If this isn\'t provided, the '
                "DJANGO_SETTINGS_MODULE environment variable will be used."
            ),
        )
        self.add_base_argument(
            parser,
            "--pythonpath",
            help=(
                "A directory to add to the Python path, e.g. "
                '"/home/djangoprojects/myproject".'
            ),
        )
        self.add_base_argument(
            parser,
            "--traceback",
            action="store_true",
            help="Raise on CommandError exceptions.",
        )
        self.add_base_argument(
            parser,
            "--no-color",
            action="store_true",
            help="Don't colorize the command output.",
        )
        self.add_base_argument(
            parser,
            "--force-color",
            action="store_true",
            help="Force colorization of the command output.",
        )
        if self.requires_system_checks:
            parser.add_argument(
                "--skip-checks",
                action="store_true",
                help="Skip system checks.",
            )
        self.add_arguments(parser)
        return parser

    def add_arguments(self, parser):
        
        pass

    def add_base_argument(self, parser, *args, **kwargs):
        
        for arg in args:
            if arg in self.suppressed_base_arguments:
                kwargs["help"] = argparse.SUPPRESS
                break
        parser.add_argument(*args, **kwargs)

    def print_help(self, prog_name, subcommand):
        
        parser = self.create_parser(prog_name, subcommand)
        parser.print_help()

    def run_from_argv(self, argv):
        
        self._called_from_command_line = True
        parser = self.create_parser(argv[0], argv[1])

        options = parser.parse_args(argv[2:])
        cmd_options = vars(options)
        
        args = cmd_options.pop("args", ())
        handle_default_options(options)
        try:
            self.execute(*args, **cmd_options)
        except CommandError as e:
            if options.traceback:
                raise

            
            if isinstance(e, SystemCheckError):
                self.stderr.write(str(e), lambda x: x)
            else:
                self.stderr.write("%s: %s" % (e.__class__.__name__, e))
            sys.exit(e.returncode)
        finally:
            try:
                connections.close_all()
            except ImproperlyConfigured:
                
                
                pass

    def execute(self, *args, **options):
        
        if options["force_color"] and options["no_color"]:
            raise CommandError(
                "The --no-color and --force-color options can't be used together."
            )
        if options["force_color"]:
            self.style = color_style(force_color=True)
        elif options["no_color"]:
            self.style = no_style()
            self.stderr.style_func = None
        if options.get("stdout"):
            self.stdout = OutputWrapper(options["stdout"])
        if options.get("stderr"):
            self.stderr = OutputWrapper(options["stderr"])

        if self.requires_system_checks and not options["skip_checks"]:
            if self.requires_system_checks == ALL_CHECKS:
                self.check()
            else:
                self.check(tags=self.requires_system_checks)
        if self.requires_migrations_checks:
            self.check_migrations()
        output = self.handle(*args, **options)
        if output:
            if self.output_transaction:
                connection = connections[options.get("database", DEFAULT_DB_ALIAS)]
                output = "%s\n%s\n%s" % (
                    self.style.SQL_KEYWORD(connection.ops.start_transaction_sql()),
                    output,
                    self.style.SQL_KEYWORD(connection.ops.end_transaction_sql()),
                )
            self.stdout.write(output)
        return output

    def check(
        self,
        app_configs=None,
        tags=None,
        display_num_errors=False,
        include_deployment_checks=False,
        fail_level=checks.ERROR,
        databases=None,
    ):
        
        all_issues = checks.run_checks(
            app_configs=app_configs,
            tags=tags,
            include_deployment_checks=include_deployment_checks,
            databases=databases,
        )

        header, body, footer = "", "", ""
        visible_issue_count = 0  

        if all_issues:
            debugs = [
                e for e in all_issues if e.level < checks.INFO and not e.is_silenced()
            ]
            infos = [
                e
                for e in all_issues
                if checks.INFO <= e.level < checks.WARNING and not e.is_silenced()
            ]
            warnings = [
                e
                for e in all_issues
                if checks.WARNING <= e.level < checks.ERROR and not e.is_silenced()
            ]
            errors = [
                e
                for e in all_issues
                if checks.ERROR <= e.level < checks.CRITICAL and not e.is_silenced()
            ]
            criticals = [
                e
                for e in all_issues
                if checks.CRITICAL <= e.level and not e.is_silenced()
            ]
            sorted_issues = [
                (criticals, "CRITICALS"),
                (errors, "ERRORS"),
                (warnings, "WARNINGS"),
                (infos, "INFOS"),
                (debugs, "DEBUGS"),
            ]

            for issues, group_name in sorted_issues:
                if issues:
                    visible_issue_count += len(issues)
                    formatted = (
                        self.style.ERROR(str(e))
                        if e.is_serious()
                        else self.style.WARNING(str(e))
                        for e in issues
                    )
                    formatted = "\n".join(sorted(formatted))
                    body += "\n%s:\n%s\n" % (group_name, formatted)

        if visible_issue_count:
            header = "System check identified some issues:\n"

        if display_num_errors:
            if visible_issue_count:
                footer += "\n"
            footer += "System check identified %s (%s silenced)." % (
                "no issues"
                if visible_issue_count == 0
                else "1 issue"
                if visible_issue_count == 1
                else "%s issues" % visible_issue_count,
                len(all_issues) - visible_issue_count,
            )

        if any(e.is_serious(fail_level) and not e.is_silenced() for e in all_issues):
            msg = self.style.ERROR("SystemCheckError: %s" % header) + body + footer
            raise SystemCheckError(msg)
        else:
            msg = header + body + footer

        if msg:
            if visible_issue_count:
                self.stderr.write(msg, lambda x: x)
            else:
                self.stdout.write(msg)

    def check_migrations(self):
        
        from django.db.migrations.executor import MigrationExecutor

        try:
            executor = MigrationExecutor(connections[DEFAULT_DB_ALIAS])
        except ImproperlyConfigured:
            
            return

        plan = executor.migration_plan(executor.loader.graph.leaf_nodes())
        if plan:
            apps_waiting_migration = sorted(
                {migration.app_label for migration, backwards in plan}
            )
            self.stdout.write(
                self.style.NOTICE(
                    "\nYou have %(unapplied_migration_count)s unapplied migration(s). "
                    "Your project may not work properly until you apply the "
                    "migrations for app(s): %(apps_waiting_migration)s."
                    % {
                        "unapplied_migration_count": len(plan),
                        "apps_waiting_migration": ", ".join(apps_waiting_migration),
                    }
                )
            )
            self.stdout.write(
                self.style.NOTICE("Run 'python manage.py migrate' to apply them.")
            )

    def handle(self, *args, **options):
        
        raise NotImplementedError(
            "subclasses of BaseCommand must provide a handle() method"
        )


class AppCommand(BaseCommand):
    

    missing_args_message = "Enter at least one application label."

    def add_arguments(self, parser):
        parser.add_argument(
            "args",
            metavar="app_label",
            nargs="+",
            help="One or more application label.",
        )

    def handle(self, *app_labels, **options):
        from django.apps import apps

        try:
            app_configs = [apps.get_app_config(app_label) for app_label in app_labels]
        except (LookupError, ImportError) as e:
            raise CommandError(
                "%s. Are you sure your INSTALLED_APPS setting is correct?" % e
            )
        output = []
        for app_config in app_configs:
            app_output = self.handle_app_config(app_config, **options)
            if app_output:
                output.append(app_output)
        return "\n".join(output)

    def handle_app_config(self, app_config, **options):
        
        raise NotImplementedError(
            "Subclasses of AppCommand must provide a handle_app_config() method."
        )


class LabelCommand(BaseCommand):
    

    label = "label"
    missing_args_message = "Enter at least one %s." % label

    def add_arguments(self, parser):
        parser.add_argument("args", metavar=self.label, nargs="+")

    def handle(self, *labels, **options):
        output = []
        for label in labels:
            label_output = self.handle_label(label, **options)
            if label_output:
                output.append(label_output)
        return "\n".join(output)

    def handle_label(self, label, **options):
        
        raise NotImplementedError(
            "subclasses of LabelCommand must provide a handle_label() method"
        )