plain 0.3.1__py3-none-any.whl → 0.4.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- plain/cli/README.md +0 -4
- plain/cli/cli.py +0 -20
- {plain-0.3.1.dist-info → plain-0.4.0.dist-info}/METADATA +1 -1
- {plain-0.3.1.dist-info → plain-0.4.0.dist-info}/RECORD +7 -14
- plain/internal/legacy/__init__.py +0 -0
- plain/internal/legacy/__main__.py +0 -12
- plain/internal/legacy/management/__init__.py +0 -414
- plain/internal/legacy/management/base.py +0 -692
- plain/internal/legacy/management/color.py +0 -113
- plain/internal/legacy/management/sql.py +0 -67
- plain/internal/legacy/management/utils.py +0 -175
- {plain-0.3.1.dist-info → plain-0.4.0.dist-info}/LICENSE +0 -0
- {plain-0.3.1.dist-info → plain-0.4.0.dist-info}/WHEEL +0 -0
- {plain-0.3.1.dist-info → plain-0.4.0.dist-info}/entry_points.txt +0 -0
@@ -1,692 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
Base classes for writing management commands (named commands which can
|
3
|
-
be executed through ``django-admin`` or ``manage.py``).
|
4
|
-
"""
|
5
|
-
import argparse
|
6
|
-
import os
|
7
|
-
import sys
|
8
|
-
from argparse import ArgumentParser, HelpFormatter
|
9
|
-
from functools import partial
|
10
|
-
from io import TextIOBase
|
11
|
-
|
12
|
-
import plain.runtime
|
13
|
-
from plain import preflight
|
14
|
-
from plain.exceptions import ImproperlyConfigured
|
15
|
-
from plain.internal.legacy.management.color import color_style, no_style
|
16
|
-
|
17
|
-
ALL_CHECKS = "__all__"
|
18
|
-
|
19
|
-
|
20
|
-
class CommandError(Exception):
|
21
|
-
"""
|
22
|
-
Exception class indicating a problem while executing a management
|
23
|
-
command.
|
24
|
-
|
25
|
-
If this exception is raised during the execution of a management
|
26
|
-
command, it will be caught and turned into a nicely-printed error
|
27
|
-
message to the appropriate output stream (i.e., stderr); as a
|
28
|
-
result, raising this exception (with a sensible description of the
|
29
|
-
error) is the preferred way to indicate that something has gone
|
30
|
-
wrong in the execution of a command.
|
31
|
-
"""
|
32
|
-
|
33
|
-
def __init__(self, *args, returncode=1, **kwargs):
|
34
|
-
self.returncode = returncode
|
35
|
-
super().__init__(*args, **kwargs)
|
36
|
-
|
37
|
-
|
38
|
-
class SystemCheckError(CommandError):
|
39
|
-
"""
|
40
|
-
The system check framework detected unrecoverable errors.
|
41
|
-
"""
|
42
|
-
|
43
|
-
pass
|
44
|
-
|
45
|
-
|
46
|
-
class CommandParser(ArgumentParser):
|
47
|
-
"""
|
48
|
-
Customized ArgumentParser class to improve some error messages and prevent
|
49
|
-
SystemExit in several occasions, as SystemExit is unacceptable when a
|
50
|
-
command is called programmatically.
|
51
|
-
"""
|
52
|
-
|
53
|
-
def __init__(
|
54
|
-
self, *, missing_args_message=None, called_from_command_line=None, **kwargs
|
55
|
-
):
|
56
|
-
self.missing_args_message = missing_args_message
|
57
|
-
self.called_from_command_line = called_from_command_line
|
58
|
-
super().__init__(**kwargs)
|
59
|
-
|
60
|
-
def parse_args(self, args=None, namespace=None):
|
61
|
-
# Catch missing argument for a better error message
|
62
|
-
if self.missing_args_message and not (
|
63
|
-
args or any(not arg.startswith("-") for arg in args)
|
64
|
-
):
|
65
|
-
self.error(self.missing_args_message)
|
66
|
-
return super().parse_args(args, namespace)
|
67
|
-
|
68
|
-
def error(self, message):
|
69
|
-
if self.called_from_command_line:
|
70
|
-
super().error(message)
|
71
|
-
else:
|
72
|
-
raise CommandError("Error: %s" % message)
|
73
|
-
|
74
|
-
def add_subparsers(self, **kwargs):
|
75
|
-
parser_class = kwargs.get("parser_class", type(self))
|
76
|
-
if issubclass(parser_class, CommandParser):
|
77
|
-
kwargs["parser_class"] = partial(
|
78
|
-
parser_class,
|
79
|
-
called_from_command_line=self.called_from_command_line,
|
80
|
-
)
|
81
|
-
return super().add_subparsers(**kwargs)
|
82
|
-
|
83
|
-
|
84
|
-
def handle_default_options(options):
|
85
|
-
"""
|
86
|
-
Include any default options that all commands should accept here
|
87
|
-
so that ManagementUtility can handle them before searching for
|
88
|
-
user commands.
|
89
|
-
"""
|
90
|
-
if options.settings:
|
91
|
-
os.environ["PLAIN_SETTINGS_MODULE"] = options.settings
|
92
|
-
if options.pythonpath:
|
93
|
-
sys.path.insert(0, options.pythonpath)
|
94
|
-
|
95
|
-
|
96
|
-
def no_translations(handle_func):
|
97
|
-
"""Decorator that forces a command to run with translations deactivated."""
|
98
|
-
|
99
|
-
def wrapper(*args, **kwargs):
|
100
|
-
return handle_func(*args, **kwargs)
|
101
|
-
|
102
|
-
return wrapper
|
103
|
-
|
104
|
-
|
105
|
-
class DjangoHelpFormatter(HelpFormatter):
|
106
|
-
"""
|
107
|
-
Customized formatter so that command-specific arguments appear in the
|
108
|
-
--help output before arguments common to all commands.
|
109
|
-
"""
|
110
|
-
|
111
|
-
show_last = {
|
112
|
-
"--version",
|
113
|
-
"--verbosity",
|
114
|
-
"--traceback",
|
115
|
-
"--settings",
|
116
|
-
"--pythonpath",
|
117
|
-
"--no-color",
|
118
|
-
"--force-color",
|
119
|
-
"--skip-checks",
|
120
|
-
}
|
121
|
-
|
122
|
-
def _reordered_actions(self, actions):
|
123
|
-
return sorted(
|
124
|
-
actions, key=lambda a: set(a.option_strings) & self.show_last != set()
|
125
|
-
)
|
126
|
-
|
127
|
-
def add_usage(self, usage, actions, *args, **kwargs):
|
128
|
-
super().add_usage(usage, self._reordered_actions(actions), *args, **kwargs)
|
129
|
-
|
130
|
-
def add_arguments(self, actions):
|
131
|
-
super().add_arguments(self._reordered_actions(actions))
|
132
|
-
|
133
|
-
|
134
|
-
class OutputWrapper(TextIOBase):
|
135
|
-
"""
|
136
|
-
Wrapper around stdout/stderr
|
137
|
-
"""
|
138
|
-
|
139
|
-
@property
|
140
|
-
def style_func(self):
|
141
|
-
return self._style_func
|
142
|
-
|
143
|
-
@style_func.setter
|
144
|
-
def style_func(self, style_func):
|
145
|
-
if style_func and self.isatty():
|
146
|
-
self._style_func = style_func
|
147
|
-
else:
|
148
|
-
self._style_func = lambda x: x
|
149
|
-
|
150
|
-
def __init__(self, out, ending="\n"):
|
151
|
-
self._out = out
|
152
|
-
self.style_func = None
|
153
|
-
self.ending = ending
|
154
|
-
|
155
|
-
def __getattr__(self, name):
|
156
|
-
return getattr(self._out, name)
|
157
|
-
|
158
|
-
def flush(self):
|
159
|
-
if hasattr(self._out, "flush"):
|
160
|
-
self._out.flush()
|
161
|
-
|
162
|
-
def isatty(self):
|
163
|
-
return hasattr(self._out, "isatty") and self._out.isatty()
|
164
|
-
|
165
|
-
def write(self, msg="", style_func=None, ending=None):
|
166
|
-
ending = self.ending if ending is None else ending
|
167
|
-
if ending and not msg.endswith(ending):
|
168
|
-
msg += ending
|
169
|
-
style_func = style_func or self.style_func
|
170
|
-
self._out.write(style_func(msg))
|
171
|
-
|
172
|
-
|
173
|
-
class BaseCommand:
|
174
|
-
"""
|
175
|
-
The base class from which all management commands ultimately
|
176
|
-
derive.
|
177
|
-
|
178
|
-
Use this class if you want access to all of the mechanisms which
|
179
|
-
parse the command-line arguments and work out what code to call in
|
180
|
-
response; if you don't need to change any of that behavior,
|
181
|
-
consider using one of the subclasses defined in this file.
|
182
|
-
|
183
|
-
If you are interested in overriding/customizing various aspects of
|
184
|
-
the command-parsing and -execution behavior, the normal flow works
|
185
|
-
as follows:
|
186
|
-
|
187
|
-
1. ``django-admin`` or ``manage.py`` loads the command class
|
188
|
-
and calls its ``run_from_argv()`` method.
|
189
|
-
|
190
|
-
2. The ``run_from_argv()`` method calls ``create_parser()`` to get
|
191
|
-
an ``ArgumentParser`` for the arguments, parses them, performs
|
192
|
-
any environment changes requested by options like
|
193
|
-
``pythonpath``, and then calls the ``execute()`` method,
|
194
|
-
passing the parsed arguments.
|
195
|
-
|
196
|
-
3. The ``execute()`` method attempts to carry out the command by
|
197
|
-
calling the ``handle()`` method with the parsed arguments; any
|
198
|
-
output produced by ``handle()`` will be printed to standard
|
199
|
-
output and, if the command is intended to produce a block of
|
200
|
-
SQL statements, will be wrapped in ``BEGIN`` and ``COMMIT``.
|
201
|
-
|
202
|
-
4. If ``handle()`` or ``execute()`` raised any exception (e.g.
|
203
|
-
``CommandError``), ``run_from_argv()`` will instead print an error
|
204
|
-
message to ``stderr``.
|
205
|
-
|
206
|
-
Thus, the ``handle()`` method is typically the starting point for
|
207
|
-
subclasses; many built-in commands and command types either place
|
208
|
-
all of their logic in ``handle()``, or perform some additional
|
209
|
-
parsing work in ``handle()`` and then delegate from it to more
|
210
|
-
specialized methods as needed.
|
211
|
-
|
212
|
-
Several attributes affect behavior at various steps along the way:
|
213
|
-
|
214
|
-
``help``
|
215
|
-
A short description of the command, which will be printed in
|
216
|
-
help messages.
|
217
|
-
|
218
|
-
``output_transaction``
|
219
|
-
A boolean indicating whether the command outputs SQL
|
220
|
-
statements; if ``True``, the output will automatically be
|
221
|
-
wrapped with ``BEGIN;`` and ``COMMIT;``. Default value is
|
222
|
-
``False``.
|
223
|
-
|
224
|
-
``requires_migrations_checks``
|
225
|
-
A boolean; if ``True``, the command prints a warning if the set of
|
226
|
-
migrations on disk don't match the migrations in the database.
|
227
|
-
|
228
|
-
``requires_system_checks``
|
229
|
-
A list or tuple of tags, e.g. [Tags.assets, Tags.models]. System
|
230
|
-
checks registered in the chosen tags will be checked for errors prior
|
231
|
-
to executing the command. The value '__all__' can be used to specify
|
232
|
-
that all system checks should be performed. Default value is '__all__'.
|
233
|
-
|
234
|
-
To validate an individual application's models
|
235
|
-
rather than all applications' models, call
|
236
|
-
``self.check(package_configs)`` from ``handle()``, where ``package_configs``
|
237
|
-
is the list of application's configuration provided by the
|
238
|
-
app registry.
|
239
|
-
|
240
|
-
``stealth_options``
|
241
|
-
A tuple of any options the command uses which aren't defined by the
|
242
|
-
argument parser.
|
243
|
-
"""
|
244
|
-
|
245
|
-
# Metadata about this command.
|
246
|
-
help = ""
|
247
|
-
|
248
|
-
# Configuration shortcuts that alter various logic.
|
249
|
-
_called_from_command_line = False
|
250
|
-
output_transaction = False # Whether to wrap the output in a "BEGIN; COMMIT;"
|
251
|
-
requires_migrations_checks = False
|
252
|
-
requires_system_checks = "__all__"
|
253
|
-
# Arguments, common to all commands, which aren't defined by the argument
|
254
|
-
# parser.
|
255
|
-
base_stealth_options = ("stderr", "stdout")
|
256
|
-
# Command-specific options not defined by the argument parser.
|
257
|
-
stealth_options = ()
|
258
|
-
suppressed_base_arguments = set()
|
259
|
-
|
260
|
-
def __init__(self, stdout=None, stderr=None, no_color=False, force_color=False):
|
261
|
-
self.stdout = OutputWrapper(stdout or sys.stdout)
|
262
|
-
self.stderr = OutputWrapper(stderr or sys.stderr)
|
263
|
-
if no_color and force_color:
|
264
|
-
raise CommandError("'no_color' and 'force_color' can't be used together.")
|
265
|
-
if no_color:
|
266
|
-
self.style = no_style()
|
267
|
-
else:
|
268
|
-
self.style = color_style(force_color)
|
269
|
-
self.stderr.style_func = self.style.ERROR
|
270
|
-
if (
|
271
|
-
not isinstance(self.requires_system_checks, list | tuple)
|
272
|
-
and self.requires_system_checks != ALL_CHECKS
|
273
|
-
):
|
274
|
-
raise TypeError("requires_system_checks must be a list or tuple.")
|
275
|
-
|
276
|
-
def get_version(self):
|
277
|
-
"""
|
278
|
-
Return the Plain version, which should be correct for all built-in
|
279
|
-
Plain commands. User-supplied commands can override this method to
|
280
|
-
return their own version.
|
281
|
-
"""
|
282
|
-
return plain.runtime.__version__
|
283
|
-
|
284
|
-
def create_parser(self, prog_name, subcommand, **kwargs):
|
285
|
-
"""
|
286
|
-
Create and return the ``ArgumentParser`` which will be used to
|
287
|
-
parse the arguments to this command.
|
288
|
-
"""
|
289
|
-
kwargs.setdefault("formatter_class", DjangoHelpFormatter)
|
290
|
-
parser = CommandParser(
|
291
|
-
prog=f"{os.path.basename(prog_name)} {subcommand}",
|
292
|
-
description=self.help or None,
|
293
|
-
missing_args_message=getattr(self, "missing_args_message", None),
|
294
|
-
called_from_command_line=getattr(self, "_called_from_command_line", None),
|
295
|
-
**kwargs,
|
296
|
-
)
|
297
|
-
self.add_base_argument(
|
298
|
-
parser,
|
299
|
-
"--version",
|
300
|
-
action="version",
|
301
|
-
version=self.get_version(),
|
302
|
-
help="Show program's version number and exit.",
|
303
|
-
)
|
304
|
-
self.add_base_argument(
|
305
|
-
parser,
|
306
|
-
"-v",
|
307
|
-
"--verbosity",
|
308
|
-
default=1,
|
309
|
-
type=int,
|
310
|
-
choices=[0, 1, 2, 3],
|
311
|
-
help=(
|
312
|
-
"Verbosity level; 0=minimal output, 1=normal output, 2=verbose output, "
|
313
|
-
"3=very verbose output"
|
314
|
-
),
|
315
|
-
)
|
316
|
-
self.add_base_argument(
|
317
|
-
parser,
|
318
|
-
"--settings",
|
319
|
-
help=(
|
320
|
-
"The Python path to a settings module, e.g. "
|
321
|
-
'"myproject.settings.main". If this isn\'t provided, the '
|
322
|
-
"PLAIN_SETTINGS_MODULE environment variable will be used."
|
323
|
-
),
|
324
|
-
)
|
325
|
-
self.add_base_argument(
|
326
|
-
parser,
|
327
|
-
"--pythonpath",
|
328
|
-
help=(
|
329
|
-
"A directory to add to the Python path, e.g. "
|
330
|
-
'"/home/djangoprojects/myproject".'
|
331
|
-
),
|
332
|
-
)
|
333
|
-
self.add_base_argument(
|
334
|
-
parser,
|
335
|
-
"--traceback",
|
336
|
-
action="store_true",
|
337
|
-
help="Raise on CommandError exceptions.",
|
338
|
-
)
|
339
|
-
self.add_base_argument(
|
340
|
-
parser,
|
341
|
-
"--no-color",
|
342
|
-
action="store_true",
|
343
|
-
help="Don't colorize the command output.",
|
344
|
-
)
|
345
|
-
self.add_base_argument(
|
346
|
-
parser,
|
347
|
-
"--force-color",
|
348
|
-
action="store_true",
|
349
|
-
help="Force colorization of the command output.",
|
350
|
-
)
|
351
|
-
if self.requires_system_checks:
|
352
|
-
parser.add_argument(
|
353
|
-
"--skip-checks",
|
354
|
-
action="store_true",
|
355
|
-
help="Skip system checks.",
|
356
|
-
)
|
357
|
-
self.add_arguments(parser)
|
358
|
-
return parser
|
359
|
-
|
360
|
-
def add_arguments(self, parser):
|
361
|
-
"""
|
362
|
-
Entry point for subclassed commands to add custom arguments.
|
363
|
-
"""
|
364
|
-
pass
|
365
|
-
|
366
|
-
def add_base_argument(self, parser, *args, **kwargs):
|
367
|
-
"""
|
368
|
-
Call the parser's add_argument() method, suppressing the help text
|
369
|
-
according to BaseCommand.suppressed_base_arguments.
|
370
|
-
"""
|
371
|
-
for arg in args:
|
372
|
-
if arg in self.suppressed_base_arguments:
|
373
|
-
kwargs["help"] = argparse.SUPPRESS
|
374
|
-
break
|
375
|
-
parser.add_argument(*args, **kwargs)
|
376
|
-
|
377
|
-
def print_help(self, prog_name, subcommand):
|
378
|
-
"""
|
379
|
-
Print the help message for this command, derived from
|
380
|
-
``self.usage()``.
|
381
|
-
"""
|
382
|
-
parser = self.create_parser(prog_name, subcommand)
|
383
|
-
parser.print_help()
|
384
|
-
|
385
|
-
def run_from_argv(self, argv):
|
386
|
-
"""
|
387
|
-
Set up any environment changes requested (e.g., Python path
|
388
|
-
and Plain settings), then run this command. If the
|
389
|
-
command raises a ``CommandError``, intercept it and print it sensibly
|
390
|
-
to stderr. If the ``--traceback`` option is present or the raised
|
391
|
-
``Exception`` is not ``CommandError``, raise it.
|
392
|
-
"""
|
393
|
-
self._called_from_command_line = True
|
394
|
-
parser = self.create_parser(argv[0], argv[1])
|
395
|
-
|
396
|
-
options = parser.parse_args(argv[2:])
|
397
|
-
cmd_options = vars(options)
|
398
|
-
# Move positional args out of options to mimic legacy optparse
|
399
|
-
args = cmd_options.pop("args", ())
|
400
|
-
handle_default_options(options)
|
401
|
-
try:
|
402
|
-
self.execute(*args, **cmd_options)
|
403
|
-
except CommandError as e:
|
404
|
-
if options.traceback:
|
405
|
-
raise
|
406
|
-
|
407
|
-
# SystemCheckError takes care of its own formatting.
|
408
|
-
if isinstance(e, SystemCheckError):
|
409
|
-
self.stderr.write(str(e), lambda x: x)
|
410
|
-
else:
|
411
|
-
self.stderr.write(f"{e.__class__.__name__}: {e}")
|
412
|
-
sys.exit(e.returncode)
|
413
|
-
finally:
|
414
|
-
try:
|
415
|
-
from plain.models import connections
|
416
|
-
|
417
|
-
connections.close_all()
|
418
|
-
except (ImproperlyConfigured, ImportError):
|
419
|
-
# Ignore if connections aren't setup at this point (e.g. no
|
420
|
-
# configured settings).
|
421
|
-
pass
|
422
|
-
|
423
|
-
def execute(self, *args, **options):
|
424
|
-
"""
|
425
|
-
Try to execute this command, performing system checks if needed (as
|
426
|
-
controlled by the ``requires_system_checks`` attribute, except if
|
427
|
-
force-skipped).
|
428
|
-
"""
|
429
|
-
if options["force_color"] and options["no_color"]:
|
430
|
-
raise CommandError(
|
431
|
-
"The --no-color and --force-color options can't be used together."
|
432
|
-
)
|
433
|
-
if options["force_color"]:
|
434
|
-
self.style = color_style(force_color=True)
|
435
|
-
elif options["no_color"]:
|
436
|
-
self.style = no_style()
|
437
|
-
self.stderr.style_func = None
|
438
|
-
if options.get("stdout"):
|
439
|
-
self.stdout = OutputWrapper(options["stdout"])
|
440
|
-
if options.get("stderr"):
|
441
|
-
self.stderr = OutputWrapper(options["stderr"])
|
442
|
-
|
443
|
-
if self.requires_system_checks and not options["skip_checks"]:
|
444
|
-
self.check()
|
445
|
-
if self.requires_migrations_checks:
|
446
|
-
self.check_migrations()
|
447
|
-
output = self.handle(*args, **options)
|
448
|
-
if output:
|
449
|
-
if self.output_transaction:
|
450
|
-
try:
|
451
|
-
from plain.models import DEFAULT_DB_ALIAS, connections
|
452
|
-
except ImportError:
|
453
|
-
self.stdout.write(output)
|
454
|
-
return output
|
455
|
-
connection = connections[options.get("database", DEFAULT_DB_ALIAS)]
|
456
|
-
output = "{}\n{}\n{}".format(
|
457
|
-
self.style.SQL_KEYWORD(connection.ops.start_transaction_sql()),
|
458
|
-
output,
|
459
|
-
self.style.SQL_KEYWORD(connection.ops.end_transaction_sql()),
|
460
|
-
)
|
461
|
-
self.stdout.write(output)
|
462
|
-
return output
|
463
|
-
|
464
|
-
def check(
|
465
|
-
self,
|
466
|
-
package_configs=None,
|
467
|
-
display_num_errors=False,
|
468
|
-
include_deployment_checks=False,
|
469
|
-
fail_level=preflight.ERROR,
|
470
|
-
databases=None,
|
471
|
-
):
|
472
|
-
"""
|
473
|
-
Use the system check framework to validate entire Plain project.
|
474
|
-
Raise CommandError for any serious message (error or critical errors).
|
475
|
-
If there are only light messages (like warnings), print them to stderr
|
476
|
-
and don't raise an exception.
|
477
|
-
"""
|
478
|
-
all_issues = preflight.run_checks(
|
479
|
-
package_configs=package_configs,
|
480
|
-
include_deployment_checks=include_deployment_checks,
|
481
|
-
databases=databases,
|
482
|
-
)
|
483
|
-
|
484
|
-
header, body, footer = "", "", ""
|
485
|
-
visible_issue_count = 0 # excludes silenced warnings
|
486
|
-
|
487
|
-
if all_issues:
|
488
|
-
debugs = [
|
489
|
-
e
|
490
|
-
for e in all_issues
|
491
|
-
if e.level < preflight.INFO and not e.is_silenced()
|
492
|
-
]
|
493
|
-
infos = [
|
494
|
-
e
|
495
|
-
for e in all_issues
|
496
|
-
if preflight.INFO <= e.level < preflight.WARNING and not e.is_silenced()
|
497
|
-
]
|
498
|
-
warnings = [
|
499
|
-
e
|
500
|
-
for e in all_issues
|
501
|
-
if preflight.WARNING <= e.level < preflight.ERROR
|
502
|
-
and not e.is_silenced()
|
503
|
-
]
|
504
|
-
errors = [
|
505
|
-
e
|
506
|
-
for e in all_issues
|
507
|
-
if preflight.ERROR <= e.level < preflight.CRITICAL
|
508
|
-
and not e.is_silenced()
|
509
|
-
]
|
510
|
-
criticals = [
|
511
|
-
e
|
512
|
-
for e in all_issues
|
513
|
-
if preflight.CRITICAL <= e.level and not e.is_silenced()
|
514
|
-
]
|
515
|
-
sorted_issues = [
|
516
|
-
(criticals, "CRITICALS"),
|
517
|
-
(errors, "ERRORS"),
|
518
|
-
(warnings, "WARNINGS"),
|
519
|
-
(infos, "INFOS"),
|
520
|
-
(debugs, "DEBUGS"),
|
521
|
-
]
|
522
|
-
|
523
|
-
for issues, group_name in sorted_issues:
|
524
|
-
if issues:
|
525
|
-
visible_issue_count += len(issues)
|
526
|
-
formatted = (
|
527
|
-
self.style.ERROR(str(e))
|
528
|
-
if e.is_serious()
|
529
|
-
else self.style.WARNING(str(e))
|
530
|
-
for e in issues
|
531
|
-
)
|
532
|
-
formatted = "\n".join(sorted(formatted))
|
533
|
-
body += f"\n{group_name}:\n{formatted}\n"
|
534
|
-
|
535
|
-
if visible_issue_count:
|
536
|
-
header = "System check identified some issues:\n"
|
537
|
-
|
538
|
-
if display_num_errors:
|
539
|
-
if visible_issue_count:
|
540
|
-
footer += "\n"
|
541
|
-
footer += "System check identified {} ({} silenced).".format(
|
542
|
-
"no issues"
|
543
|
-
if visible_issue_count == 0
|
544
|
-
else "1 issue"
|
545
|
-
if visible_issue_count == 1
|
546
|
-
else "%s issues" % visible_issue_count,
|
547
|
-
len(all_issues) - visible_issue_count,
|
548
|
-
)
|
549
|
-
|
550
|
-
if any(e.is_serious(fail_level) and not e.is_silenced() for e in all_issues):
|
551
|
-
msg = self.style.ERROR("SystemCheckError: %s" % header) + body + footer
|
552
|
-
raise SystemCheckError(msg)
|
553
|
-
else:
|
554
|
-
msg = header + body + footer
|
555
|
-
|
556
|
-
if msg:
|
557
|
-
if visible_issue_count:
|
558
|
-
self.stderr.write(msg, lambda x: x)
|
559
|
-
else:
|
560
|
-
self.stdout.write(msg)
|
561
|
-
|
562
|
-
def check_migrations(self):
|
563
|
-
"""
|
564
|
-
Print a warning if the set of migrations on disk don't match the
|
565
|
-
migrations in the database.
|
566
|
-
"""
|
567
|
-
try:
|
568
|
-
from plain.models import DEFAULT_DB_ALIAS, connections
|
569
|
-
from plain.models.migrations.executor import MigrationExecutor
|
570
|
-
except ImportError:
|
571
|
-
return
|
572
|
-
|
573
|
-
try:
|
574
|
-
executor = MigrationExecutor(connections[DEFAULT_DB_ALIAS])
|
575
|
-
except ImproperlyConfigured:
|
576
|
-
# No databases are configured (or the dummy one)
|
577
|
-
return
|
578
|
-
|
579
|
-
plan = executor.migration_plan(executor.loader.graph.leaf_nodes())
|
580
|
-
if plan:
|
581
|
-
packages_waiting_migration = sorted(
|
582
|
-
{migration.package_label for migration, backwards in plan}
|
583
|
-
)
|
584
|
-
self.stdout.write(
|
585
|
-
self.style.NOTICE(
|
586
|
-
"\nYou have {unapplied_migration_count} unapplied migration(s). "
|
587
|
-
"Your project may not work properly until you apply the "
|
588
|
-
"migrations for app(s): {packages_waiting_migration}.".format(
|
589
|
-
unapplied_migration_count=len(plan),
|
590
|
-
packages_waiting_migration=", ".join(
|
591
|
-
packages_waiting_migration
|
592
|
-
),
|
593
|
-
)
|
594
|
-
)
|
595
|
-
)
|
596
|
-
self.stdout.write(
|
597
|
-
self.style.NOTICE("Run 'python manage.py migrate' to apply them.")
|
598
|
-
)
|
599
|
-
|
600
|
-
def handle(self, *args, **options):
|
601
|
-
"""
|
602
|
-
The actual logic of the command. Subclasses must implement
|
603
|
-
this method.
|
604
|
-
"""
|
605
|
-
raise NotImplementedError(
|
606
|
-
"subclasses of BaseCommand must provide a handle() method"
|
607
|
-
)
|
608
|
-
|
609
|
-
|
610
|
-
class AppCommand(BaseCommand):
|
611
|
-
"""
|
612
|
-
A management command which takes one or more installed application labels
|
613
|
-
as arguments, and does something with each of them.
|
614
|
-
|
615
|
-
Rather than implementing ``handle()``, subclasses must implement
|
616
|
-
``handle_package_config()``, which will be called once for each application.
|
617
|
-
"""
|
618
|
-
|
619
|
-
missing_args_message = "Enter at least one application label."
|
620
|
-
|
621
|
-
def add_arguments(self, parser):
|
622
|
-
parser.add_argument(
|
623
|
-
"args",
|
624
|
-
metavar="package_label",
|
625
|
-
nargs="+",
|
626
|
-
help="One or more application label.",
|
627
|
-
)
|
628
|
-
|
629
|
-
def handle(self, *package_labels, **options):
|
630
|
-
from plain.packages import packages
|
631
|
-
|
632
|
-
try:
|
633
|
-
package_configs = [
|
634
|
-
packages.get_package_config(package_label)
|
635
|
-
for package_label in package_labels
|
636
|
-
]
|
637
|
-
except (LookupError, ImportError) as e:
|
638
|
-
raise CommandError(
|
639
|
-
"%s. Are you sure your INSTALLED_PACKAGES setting is correct?" % e
|
640
|
-
)
|
641
|
-
output = []
|
642
|
-
for package_config in package_configs:
|
643
|
-
app_output = self.handle_package_config(package_config, **options)
|
644
|
-
if app_output:
|
645
|
-
output.append(app_output)
|
646
|
-
return "\n".join(output)
|
647
|
-
|
648
|
-
def handle_package_config(self, package_config, **options):
|
649
|
-
"""
|
650
|
-
Perform the command's actions for package_config, an PackageConfig instance
|
651
|
-
corresponding to an application label given on the command line.
|
652
|
-
"""
|
653
|
-
raise NotImplementedError(
|
654
|
-
"Subclasses of AppCommand must provide a handle_package_config() method."
|
655
|
-
)
|
656
|
-
|
657
|
-
|
658
|
-
class LabelCommand(BaseCommand):
|
659
|
-
"""
|
660
|
-
A management command which takes one or more arbitrary arguments
|
661
|
-
(labels) on the command line, and does something with each of
|
662
|
-
them.
|
663
|
-
|
664
|
-
Rather than implementing ``handle()``, subclasses must implement
|
665
|
-
``handle_label()``, which will be called once for each label.
|
666
|
-
|
667
|
-
If the arguments should be names of installed applications, use
|
668
|
-
``AppCommand`` instead.
|
669
|
-
"""
|
670
|
-
|
671
|
-
label = "label"
|
672
|
-
missing_args_message = "Enter at least one %s." % label
|
673
|
-
|
674
|
-
def add_arguments(self, parser):
|
675
|
-
parser.add_argument("args", metavar=self.label, nargs="+")
|
676
|
-
|
677
|
-
def handle(self, *labels, **options):
|
678
|
-
output = []
|
679
|
-
for label in labels:
|
680
|
-
label_output = self.handle_label(label, **options)
|
681
|
-
if label_output:
|
682
|
-
output.append(label_output)
|
683
|
-
return "\n".join(output)
|
684
|
-
|
685
|
-
def handle_label(self, label, **options):
|
686
|
-
"""
|
687
|
-
Perform the command's actions for ``label``, which will be the
|
688
|
-
string as given on the command line.
|
689
|
-
"""
|
690
|
-
raise NotImplementedError(
|
691
|
-
"subclasses of LabelCommand must provide a handle_label() method"
|
692
|
-
)
|