winipedia-utils 0.1.62__py3-none-any.whl → 0.2.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.
Potentially problematic release.
This version of winipedia-utils might be problematic. Click here for more details.
- winipedia_utils/concurrent/concurrent.py +245 -242
- winipedia_utils/concurrent/multiprocessing.py +130 -115
- winipedia_utils/concurrent/multithreading.py +93 -93
- winipedia_utils/consts.py +23 -23
- winipedia_utils/data/__init__.py +1 -1
- winipedia_utils/data/dataframe/__init__.py +1 -0
- winipedia_utils/data/dataframe/cleaning.py +378 -0
- winipedia_utils/data/structures/__init__.py +1 -0
- winipedia_utils/data/structures/dicts.py +16 -0
- winipedia_utils/django/__init__.py +24 -24
- winipedia_utils/django/bulk.py +538 -538
- winipedia_utils/django/command.py +334 -334
- winipedia_utils/django/database.py +289 -289
- winipedia_utils/git/__init__.py +1 -1
- winipedia_utils/git/gitignore/__init__.py +1 -1
- winipedia_utils/git/gitignore/gitignore.py +136 -136
- winipedia_utils/git/pre_commit/__init__.py +1 -1
- winipedia_utils/git/pre_commit/config.py +70 -70
- winipedia_utils/git/pre_commit/hooks.py +109 -109
- winipedia_utils/git/pre_commit/run_hooks.py +49 -49
- winipedia_utils/iterating/__init__.py +1 -1
- winipedia_utils/iterating/iterate.py +29 -29
- winipedia_utils/logging/ansi.py +6 -6
- winipedia_utils/logging/config.py +64 -64
- winipedia_utils/logging/logger.py +26 -26
- winipedia_utils/modules/class_.py +119 -119
- winipedia_utils/modules/function.py +101 -103
- winipedia_utils/modules/module.py +379 -379
- winipedia_utils/modules/package.py +390 -390
- winipedia_utils/oop/mixins/meta.py +333 -331
- winipedia_utils/oop/mixins/mixin.py +37 -37
- winipedia_utils/os/__init__.py +1 -1
- winipedia_utils/os/os.py +63 -63
- winipedia_utils/projects/__init__.py +1 -1
- winipedia_utils/projects/poetry/__init__.py +1 -1
- winipedia_utils/projects/poetry/config.py +91 -91
- winipedia_utils/projects/poetry/poetry.py +31 -31
- winipedia_utils/projects/project.py +48 -48
- winipedia_utils/pyside/__init__.py +1 -1
- winipedia_utils/pyside/core/__init__.py +1 -1
- winipedia_utils/pyside/core/py_qiodevice.py +476 -476
- winipedia_utils/pyside/ui/__init__.py +1 -1
- winipedia_utils/pyside/ui/base/__init__.py +1 -1
- winipedia_utils/pyside/ui/base/base.py +180 -180
- winipedia_utils/pyside/ui/pages/__init__.py +1 -1
- winipedia_utils/pyside/ui/pages/base/__init__.py +1 -1
- winipedia_utils/pyside/ui/pages/base/base.py +92 -92
- winipedia_utils/pyside/ui/pages/browser.py +26 -26
- winipedia_utils/pyside/ui/pages/player.py +85 -85
- winipedia_utils/pyside/ui/widgets/__init__.py +1 -1
- winipedia_utils/pyside/ui/widgets/browser.py +243 -243
- winipedia_utils/pyside/ui/widgets/clickable_widget.py +57 -57
- winipedia_utils/pyside/ui/widgets/media_player.py +430 -423
- winipedia_utils/pyside/ui/widgets/notification.py +78 -78
- winipedia_utils/pyside/ui/windows/__init__.py +1 -1
- winipedia_utils/pyside/ui/windows/base/__init__.py +1 -1
- winipedia_utils/pyside/ui/windows/base/base.py +49 -49
- winipedia_utils/resources/__init__.py +1 -1
- winipedia_utils/resources/svgs/__init__.py +1 -1
- winipedia_utils/resources/svgs/download_arrow.svg +2 -2
- winipedia_utils/resources/svgs/exit_fullscreen_icon.svg +5 -5
- winipedia_utils/resources/svgs/fullscreen_icon.svg +2 -2
- winipedia_utils/resources/svgs/menu_icon.svg +3 -3
- winipedia_utils/resources/svgs/pause_icon.svg +3 -3
- winipedia_utils/resources/svgs/play_icon.svg +16 -16
- winipedia_utils/resources/svgs/plus_icon.svg +23 -23
- winipedia_utils/resources/svgs/svg.py +15 -15
- winipedia_utils/security/__init__.py +1 -1
- winipedia_utils/security/cryptography.py +29 -29
- winipedia_utils/security/keyring.py +70 -70
- winipedia_utils/setup.py +47 -47
- winipedia_utils/testing/assertions.py +23 -23
- winipedia_utils/testing/convention.py +177 -177
- winipedia_utils/testing/create_tests.py +291 -291
- winipedia_utils/testing/fixtures.py +28 -28
- winipedia_utils/testing/tests/base/fixtures/__init__.py +1 -1
- winipedia_utils/testing/tests/base/fixtures/fixture.py +6 -6
- winipedia_utils/testing/tests/base/fixtures/scopes/class_.py +33 -33
- winipedia_utils/testing/tests/base/fixtures/scopes/function.py +7 -7
- winipedia_utils/testing/tests/base/fixtures/scopes/module.py +31 -31
- winipedia_utils/testing/tests/base/fixtures/scopes/package.py +7 -7
- winipedia_utils/testing/tests/base/fixtures/scopes/session.py +312 -312
- winipedia_utils/testing/tests/base/utils/utils.py +82 -82
- winipedia_utils/testing/tests/conftest.py +32 -32
- winipedia_utils/text/string.py +126 -126
- {winipedia_utils-0.1.62.dist-info → winipedia_utils-0.2.0.dist-info}/METADATA +5 -4
- winipedia_utils-0.2.0.dist-info/RECORD +103 -0
- {winipedia_utils-0.1.62.dist-info → winipedia_utils-0.2.0.dist-info}/WHEEL +1 -1
- {winipedia_utils-0.1.62.dist-info → winipedia_utils-0.2.0.dist-info/licenses}/LICENSE +21 -21
- winipedia_utils/data/dataframe.py +0 -7
- winipedia_utils-0.1.62.dist-info/RECORD +0 -100
|
@@ -1,334 +1,334 @@
|
|
|
1
|
-
"""Command utilities for Django.
|
|
2
|
-
|
|
3
|
-
This module provides utility functions for working with Django commands,
|
|
4
|
-
including command execution and output handling. These utilities help with
|
|
5
|
-
managing and automating Django command-line tasks.
|
|
6
|
-
"""
|
|
7
|
-
|
|
8
|
-
import logging
|
|
9
|
-
from abc import abstractmethod
|
|
10
|
-
from argparse import ArgumentParser
|
|
11
|
-
from typing import Any, final
|
|
12
|
-
|
|
13
|
-
from django.core.management import BaseCommand
|
|
14
|
-
|
|
15
|
-
from winipedia_utils.oop.mixins.mixin import ABCLoggingMixin
|
|
16
|
-
|
|
17
|
-
logger = logging.getLogger(__name__)
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
class ABCBaseCommand(ABCLoggingMixin, BaseCommand):
|
|
21
|
-
"""Abstract base class for Django management commands with logging and validation.
|
|
22
|
-
|
|
23
|
-
This class serves as a foundation for creating Django management commands that
|
|
24
|
-
require abstract method implementation enforcement and automatic logging.
|
|
25
|
-
It combines Django's BaseCommand with ABCImplementationLoggingMixin to provide
|
|
26
|
-
both command functionality and development-time validation.
|
|
27
|
-
|
|
28
|
-
The class implements a template method pattern where common argument handling
|
|
29
|
-
and execution flow are managed by final methods, while specific implementations
|
|
30
|
-
are defined through abstract methods that subclasses must implement.
|
|
31
|
-
|
|
32
|
-
Key Features:
|
|
33
|
-
- Automatic logging of method calls with performance tracking
|
|
34
|
-
- Compile-time validation that all abstract methods are implemented
|
|
35
|
-
- Structured argument handling with base and custom arguments
|
|
36
|
-
- Template method pattern for consistent command execution flow
|
|
37
|
-
|
|
38
|
-
Inheritance Order:
|
|
39
|
-
The order of inheritance is critical: ABCImplementationLoggingMixin must
|
|
40
|
-
come before BaseCommand because Django's BaseCommand doesn't call
|
|
41
|
-
super().__init__(), so the mixin's metaclass initialization must happen
|
|
42
|
-
first to ensure proper class construction.
|
|
43
|
-
|
|
44
|
-
Example:
|
|
45
|
-
>>> class MyCommand(ABCBaseCommand):
|
|
46
|
-
... def add_command_arguments(self, parser):
|
|
47
|
-
... parser.add_argument('--my-option', help='Custom option')
|
|
48
|
-
...
|
|
49
|
-
... def handle_command(self, *args, **options):
|
|
50
|
-
... self.stdout.write('Executing my command')
|
|
51
|
-
|
|
52
|
-
Note:
|
|
53
|
-
- All methods are automatically logged with performance tracking
|
|
54
|
-
- Subclasses must implement add_command_arguments and handle_command
|
|
55
|
-
- The @final decorator prevents overriding of template methods
|
|
56
|
-
"""
|
|
57
|
-
|
|
58
|
-
@final
|
|
59
|
-
def add_arguments(self, parser: ArgumentParser) -> None:
|
|
60
|
-
"""Configure command-line arguments for the Django management command.
|
|
61
|
-
|
|
62
|
-
This method implements the template method pattern by first adding common
|
|
63
|
-
base arguments that are used across multiple commands, then delegating
|
|
64
|
-
to the abstract add_command_arguments method for command-specific arguments.
|
|
65
|
-
|
|
66
|
-
The @final decorator prevents subclasses from overriding this method,
|
|
67
|
-
ensuring consistent argument handling across all commands while still
|
|
68
|
-
allowing customization through the abstract method.
|
|
69
|
-
|
|
70
|
-
Args:
|
|
71
|
-
parser (ArgumentParser): Django's argument parser instance used to
|
|
72
|
-
define command-line options and arguments for the command.
|
|
73
|
-
|
|
74
|
-
Note:
|
|
75
|
-
- This method is final and cannot be overridden by subclasses
|
|
76
|
-
- Common arguments are added first via _add_arguments()
|
|
77
|
-
- Custom arguments are added via the abstract add_command_arguments()
|
|
78
|
-
- Subclasses must implement add_command_arguments() for specific needs
|
|
79
|
-
"""
|
|
80
|
-
# add base args that are used in most commands
|
|
81
|
-
self._add_arguments(parser)
|
|
82
|
-
|
|
83
|
-
# add additional args that are specific to the command
|
|
84
|
-
self.add_command_arguments(parser)
|
|
85
|
-
|
|
86
|
-
@final
|
|
87
|
-
def _add_arguments(self, parser: ArgumentParser) -> None:
|
|
88
|
-
"""Add common command-line arguments used across multiple commands.
|
|
89
|
-
|
|
90
|
-
This method defines base arguments that are commonly used across different
|
|
91
|
-
Django management commands. These arguments provide standard functionality
|
|
92
|
-
like dry-run mode, verbosity control, and batch processing options.
|
|
93
|
-
|
|
94
|
-
The method is final to ensure consistent base argument handling, while
|
|
95
|
-
command-specific arguments are handled through the abstract
|
|
96
|
-
add_command_arguments method.
|
|
97
|
-
|
|
98
|
-
Args:
|
|
99
|
-
parser (ArgumentParser): Django's argument parser instance to which
|
|
100
|
-
common arguments should be added.
|
|
101
|
-
|
|
102
|
-
Note:
|
|
103
|
-
- Provides standard arguments for dry-run, verbosity, and batch processing
|
|
104
|
-
- The @final decorator prevents subclasses from overriding this method
|
|
105
|
-
- Command-specific arguments should be added via add_command_arguments()
|
|
106
|
-
"""
|
|
107
|
-
parser.add_argument(
|
|
108
|
-
"--dry-run",
|
|
109
|
-
action="store_true",
|
|
110
|
-
help="Show what would be done without actually executing the changes",
|
|
111
|
-
)
|
|
112
|
-
|
|
113
|
-
parser.add_argument(
|
|
114
|
-
"--size",
|
|
115
|
-
type=int,
|
|
116
|
-
default=None,
|
|
117
|
-
help="Size of smth in a command",
|
|
118
|
-
)
|
|
119
|
-
|
|
120
|
-
parser.add_argument(
|
|
121
|
-
"--force",
|
|
122
|
-
action="store_true",
|
|
123
|
-
help="Force an action in a command",
|
|
124
|
-
)
|
|
125
|
-
|
|
126
|
-
parser.add_argument(
|
|
127
|
-
"--delete",
|
|
128
|
-
action="store_true",
|
|
129
|
-
help="Deleting smth in a command",
|
|
130
|
-
)
|
|
131
|
-
|
|
132
|
-
parser.add_argument(
|
|
133
|
-
"--quiet",
|
|
134
|
-
action="store_true",
|
|
135
|
-
help="Suppress non-error output for cleaner automation",
|
|
136
|
-
)
|
|
137
|
-
|
|
138
|
-
parser.add_argument(
|
|
139
|
-
"--debug",
|
|
140
|
-
action="store_true",
|
|
141
|
-
help="Print debug output for detailed tracing",
|
|
142
|
-
)
|
|
143
|
-
|
|
144
|
-
parser.add_argument(
|
|
145
|
-
"--yes",
|
|
146
|
-
action="store_true",
|
|
147
|
-
help="Answer yes to all prompts",
|
|
148
|
-
default=False,
|
|
149
|
-
)
|
|
150
|
-
|
|
151
|
-
parser.add_argument(
|
|
152
|
-
"--config",
|
|
153
|
-
type=str,
|
|
154
|
-
help="A configuration setup like filepath or json string for a command",
|
|
155
|
-
default=None,
|
|
156
|
-
)
|
|
157
|
-
|
|
158
|
-
parser.add_argument(
|
|
159
|
-
"--timeout",
|
|
160
|
-
type=int,
|
|
161
|
-
help="Timeout for a command",
|
|
162
|
-
default=None,
|
|
163
|
-
)
|
|
164
|
-
|
|
165
|
-
parser.add_argument(
|
|
166
|
-
"--batch-size",
|
|
167
|
-
type=int,
|
|
168
|
-
default=None,
|
|
169
|
-
help="Number of items to process in each batch",
|
|
170
|
-
)
|
|
171
|
-
|
|
172
|
-
parser.add_argument(
|
|
173
|
-
"--no-input",
|
|
174
|
-
action="store_true",
|
|
175
|
-
help="Do not prompt for user input",
|
|
176
|
-
)
|
|
177
|
-
|
|
178
|
-
parser.add_argument(
|
|
179
|
-
"--threads",
|
|
180
|
-
type=int,
|
|
181
|
-
default=None,
|
|
182
|
-
help="Number of threads to use for processing",
|
|
183
|
-
)
|
|
184
|
-
|
|
185
|
-
parser.add_argument(
|
|
186
|
-
"--processes",
|
|
187
|
-
type=int,
|
|
188
|
-
default=None,
|
|
189
|
-
help="Number of processes to use for processing",
|
|
190
|
-
)
|
|
191
|
-
|
|
192
|
-
@abstractmethod
|
|
193
|
-
def add_command_arguments(self, parser: ArgumentParser) -> None:
|
|
194
|
-
"""Add command-specific arguments to the argument parser.
|
|
195
|
-
|
|
196
|
-
This abstract method must be implemented by subclasses to define
|
|
197
|
-
command-specific command-line arguments. It is called after common
|
|
198
|
-
base arguments are added, allowing each command to customize its
|
|
199
|
-
argument interface while maintaining consistent base functionality.
|
|
200
|
-
|
|
201
|
-
Subclasses should use this method to add arguments specific to their
|
|
202
|
-
command's functionality, such as file paths, configuration options,
|
|
203
|
-
or operational flags.
|
|
204
|
-
|
|
205
|
-
Args:
|
|
206
|
-
parser (ArgumentParser): Django's argument parser instance to which
|
|
207
|
-
command-specific arguments should be added.
|
|
208
|
-
|
|
209
|
-
Example:
|
|
210
|
-
>>> def add_command_arguments(self, parser):
|
|
211
|
-
... parser.add_argument(
|
|
212
|
-
... '--input-file',
|
|
213
|
-
... type=str,
|
|
214
|
-
... required=True,
|
|
215
|
-
... help='Path to input file'
|
|
216
|
-
... )
|
|
217
|
-
... parser.add_argument(
|
|
218
|
-
... '--output-format',
|
|
219
|
-
... choices=['json', 'csv', 'xml'],
|
|
220
|
-
... default='json',
|
|
221
|
-
... help='Output format for results'
|
|
222
|
-
... )
|
|
223
|
-
|
|
224
|
-
Note:
|
|
225
|
-
- This method is abstract and must be implemented by subclasses
|
|
226
|
-
- Called after _add_arguments() adds common base arguments
|
|
227
|
-
- Should focus on command-specific functionality only
|
|
228
|
-
"""
|
|
229
|
-
|
|
230
|
-
@final
|
|
231
|
-
def handle(self, *args: Any, **options: Any) -> None:
|
|
232
|
-
"""Execute the Django management command using template method pattern.
|
|
233
|
-
|
|
234
|
-
This method implements the main execution flow for the command by first
|
|
235
|
-
calling common handling logic through _handle(), then delegating to
|
|
236
|
-
the command-specific implementation via handle_command().
|
|
237
|
-
|
|
238
|
-
The @final decorator ensures this execution pattern cannot be overridden,
|
|
239
|
-
maintaining consistent command execution flow while allowing customization
|
|
240
|
-
through the abstract handle_command method.
|
|
241
|
-
|
|
242
|
-
Args:
|
|
243
|
-
*args: Positional arguments passed from Django's command execution.
|
|
244
|
-
**options: Keyword arguments containing parsed command-line options
|
|
245
|
-
and their values as defined by add_arguments().
|
|
246
|
-
|
|
247
|
-
Note:
|
|
248
|
-
- This method is final and cannot be overridden by subclasses
|
|
249
|
-
- Common handling logic is executed first via _handle()
|
|
250
|
-
- Command-specific logic is executed via abstract handle_command()
|
|
251
|
-
- All method calls are automatically logged with performance tracking
|
|
252
|
-
"""
|
|
253
|
-
self._handle(*args, **options)
|
|
254
|
-
self.handle_command(*args, **options)
|
|
255
|
-
|
|
256
|
-
@final
|
|
257
|
-
def _handle(self, *_args: Any, **options: Any) -> None:
|
|
258
|
-
"""Execute common handling logic shared across all commands.
|
|
259
|
-
|
|
260
|
-
This method is intended to contain common processing logic that should
|
|
261
|
-
be executed before command-specific handling. Currently, it serves as
|
|
262
|
-
a placeholder for future common functionality such as logging setup,
|
|
263
|
-
validation, or shared initialization.
|
|
264
|
-
|
|
265
|
-
The method is final to ensure consistent common handling across all
|
|
266
|
-
commands, while command-specific logic is handled through the abstract
|
|
267
|
-
handle_command method.
|
|
268
|
-
|
|
269
|
-
Args:
|
|
270
|
-
*args: Positional arguments passed from Django's command execution.
|
|
271
|
-
Currently unused but reserved for future common processing.
|
|
272
|
-
**options: Keyword arguments containing parsed command-line options.
|
|
273
|
-
Currently unused but reserved for future common processing.
|
|
274
|
-
|
|
275
|
-
Note:
|
|
276
|
-
- Examples might include logging setup, database connection validation, etc.
|
|
277
|
-
- The @final decorator prevents subclasses from overriding this method
|
|
278
|
-
- Called before handle_command() in the template method pattern
|
|
279
|
-
"""
|
|
280
|
-
# log each option for the command
|
|
281
|
-
for key, value in options.items():
|
|
282
|
-
logger.info(
|
|
283
|
-
"Command '%s' - runs with option: '%s' with value: '%s'",
|
|
284
|
-
self.__class__.__name__,
|
|
285
|
-
key,
|
|
286
|
-
value,
|
|
287
|
-
)
|
|
288
|
-
|
|
289
|
-
@abstractmethod
|
|
290
|
-
def handle_command(self, *args: Any, **options: Any) -> None:
|
|
291
|
-
"""Execute command-specific logic and functionality.
|
|
292
|
-
|
|
293
|
-
This abstract method must be implemented by subclasses to define the
|
|
294
|
-
core functionality of the Django management command. It is called after
|
|
295
|
-
common handling logic is executed, allowing each command to implement
|
|
296
|
-
its specific business logic while benefiting from shared infrastructure.
|
|
297
|
-
|
|
298
|
-
This method should contain the main logic that the command is designed
|
|
299
|
-
to perform, such as data processing, database operations, file manipulation,
|
|
300
|
-
or any other command-specific tasks.
|
|
301
|
-
|
|
302
|
-
Args:
|
|
303
|
-
*args: Positional arguments passed from Django's command execution.
|
|
304
|
-
These are typically not used in Django management commands.
|
|
305
|
-
**options: Keyword arguments containing parsed command-line options
|
|
306
|
-
and their values as defined by add_command_arguments().
|
|
307
|
-
|
|
308
|
-
Example:
|
|
309
|
-
>>> def handle_command(self, *args, **options):
|
|
310
|
-
... input_file = options['input_file']
|
|
311
|
-
... dry_run = options['dry_run'] # Base argument
|
|
312
|
-
... batch_size = options['batch_size'] # Base argument
|
|
313
|
-
... quiet = options['quiet'] # Base argument
|
|
314
|
-
...
|
|
315
|
-
... if dry_run:
|
|
316
|
-
... self.stdout.write('Dry run mode - no changes will be made')
|
|
317
|
-
...
|
|
318
|
-
... if not quiet:
|
|
319
|
-
... msg = f'Processing {input_file} in batches of {batch_size}'
|
|
320
|
-
... self.stdout.write(msg)
|
|
321
|
-
...
|
|
322
|
-
... # Perform command-specific operations
|
|
323
|
-
... self.process_file(input_file, batch_size, dry_run)
|
|
324
|
-
...
|
|
325
|
-
... if not quiet:
|
|
326
|
-
... self.stdout.write('Command completed successfully')
|
|
327
|
-
|
|
328
|
-
Note:
|
|
329
|
-
- This method is abstract and must be implemented by subclasses
|
|
330
|
-
- Called after _handle() executes common logic
|
|
331
|
-
- Should contain the main functionality of the command
|
|
332
|
-
- All method calls are automatically logged with performance tracking
|
|
333
|
-
- Use self.stdout.write() for output instead of print()
|
|
334
|
-
"""
|
|
1
|
+
"""Command utilities for Django.
|
|
2
|
+
|
|
3
|
+
This module provides utility functions for working with Django commands,
|
|
4
|
+
including command execution and output handling. These utilities help with
|
|
5
|
+
managing and automating Django command-line tasks.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import logging
|
|
9
|
+
from abc import abstractmethod
|
|
10
|
+
from argparse import ArgumentParser
|
|
11
|
+
from typing import Any, final
|
|
12
|
+
|
|
13
|
+
from django.core.management import BaseCommand
|
|
14
|
+
|
|
15
|
+
from winipedia_utils.oop.mixins.mixin import ABCLoggingMixin
|
|
16
|
+
|
|
17
|
+
logger = logging.getLogger(__name__)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class ABCBaseCommand(ABCLoggingMixin, BaseCommand):
|
|
21
|
+
"""Abstract base class for Django management commands with logging and validation.
|
|
22
|
+
|
|
23
|
+
This class serves as a foundation for creating Django management commands that
|
|
24
|
+
require abstract method implementation enforcement and automatic logging.
|
|
25
|
+
It combines Django's BaseCommand with ABCImplementationLoggingMixin to provide
|
|
26
|
+
both command functionality and development-time validation.
|
|
27
|
+
|
|
28
|
+
The class implements a template method pattern where common argument handling
|
|
29
|
+
and execution flow are managed by final methods, while specific implementations
|
|
30
|
+
are defined through abstract methods that subclasses must implement.
|
|
31
|
+
|
|
32
|
+
Key Features:
|
|
33
|
+
- Automatic logging of method calls with performance tracking
|
|
34
|
+
- Compile-time validation that all abstract methods are implemented
|
|
35
|
+
- Structured argument handling with base and custom arguments
|
|
36
|
+
- Template method pattern for consistent command execution flow
|
|
37
|
+
|
|
38
|
+
Inheritance Order:
|
|
39
|
+
The order of inheritance is critical: ABCImplementationLoggingMixin must
|
|
40
|
+
come before BaseCommand because Django's BaseCommand doesn't call
|
|
41
|
+
super().__init__(), so the mixin's metaclass initialization must happen
|
|
42
|
+
first to ensure proper class construction.
|
|
43
|
+
|
|
44
|
+
Example:
|
|
45
|
+
>>> class MyCommand(ABCBaseCommand):
|
|
46
|
+
... def add_command_arguments(self, parser):
|
|
47
|
+
... parser.add_argument('--my-option', help='Custom option')
|
|
48
|
+
...
|
|
49
|
+
... def handle_command(self, *args, **options):
|
|
50
|
+
... self.stdout.write('Executing my command')
|
|
51
|
+
|
|
52
|
+
Note:
|
|
53
|
+
- All methods are automatically logged with performance tracking
|
|
54
|
+
- Subclasses must implement add_command_arguments and handle_command
|
|
55
|
+
- The @final decorator prevents overriding of template methods
|
|
56
|
+
"""
|
|
57
|
+
|
|
58
|
+
@final
|
|
59
|
+
def add_arguments(self, parser: ArgumentParser) -> None:
|
|
60
|
+
"""Configure command-line arguments for the Django management command.
|
|
61
|
+
|
|
62
|
+
This method implements the template method pattern by first adding common
|
|
63
|
+
base arguments that are used across multiple commands, then delegating
|
|
64
|
+
to the abstract add_command_arguments method for command-specific arguments.
|
|
65
|
+
|
|
66
|
+
The @final decorator prevents subclasses from overriding this method,
|
|
67
|
+
ensuring consistent argument handling across all commands while still
|
|
68
|
+
allowing customization through the abstract method.
|
|
69
|
+
|
|
70
|
+
Args:
|
|
71
|
+
parser (ArgumentParser): Django's argument parser instance used to
|
|
72
|
+
define command-line options and arguments for the command.
|
|
73
|
+
|
|
74
|
+
Note:
|
|
75
|
+
- This method is final and cannot be overridden by subclasses
|
|
76
|
+
- Common arguments are added first via _add_arguments()
|
|
77
|
+
- Custom arguments are added via the abstract add_command_arguments()
|
|
78
|
+
- Subclasses must implement add_command_arguments() for specific needs
|
|
79
|
+
"""
|
|
80
|
+
# add base args that are used in most commands
|
|
81
|
+
self._add_arguments(parser)
|
|
82
|
+
|
|
83
|
+
# add additional args that are specific to the command
|
|
84
|
+
self.add_command_arguments(parser)
|
|
85
|
+
|
|
86
|
+
@final
|
|
87
|
+
def _add_arguments(self, parser: ArgumentParser) -> None:
|
|
88
|
+
"""Add common command-line arguments used across multiple commands.
|
|
89
|
+
|
|
90
|
+
This method defines base arguments that are commonly used across different
|
|
91
|
+
Django management commands. These arguments provide standard functionality
|
|
92
|
+
like dry-run mode, verbosity control, and batch processing options.
|
|
93
|
+
|
|
94
|
+
The method is final to ensure consistent base argument handling, while
|
|
95
|
+
command-specific arguments are handled through the abstract
|
|
96
|
+
add_command_arguments method.
|
|
97
|
+
|
|
98
|
+
Args:
|
|
99
|
+
parser (ArgumentParser): Django's argument parser instance to which
|
|
100
|
+
common arguments should be added.
|
|
101
|
+
|
|
102
|
+
Note:
|
|
103
|
+
- Provides standard arguments for dry-run, verbosity, and batch processing
|
|
104
|
+
- The @final decorator prevents subclasses from overriding this method
|
|
105
|
+
- Command-specific arguments should be added via add_command_arguments()
|
|
106
|
+
"""
|
|
107
|
+
parser.add_argument(
|
|
108
|
+
"--dry-run",
|
|
109
|
+
action="store_true",
|
|
110
|
+
help="Show what would be done without actually executing the changes",
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
parser.add_argument(
|
|
114
|
+
"--size",
|
|
115
|
+
type=int,
|
|
116
|
+
default=None,
|
|
117
|
+
help="Size of smth in a command",
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
parser.add_argument(
|
|
121
|
+
"--force",
|
|
122
|
+
action="store_true",
|
|
123
|
+
help="Force an action in a command",
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
parser.add_argument(
|
|
127
|
+
"--delete",
|
|
128
|
+
action="store_true",
|
|
129
|
+
help="Deleting smth in a command",
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
parser.add_argument(
|
|
133
|
+
"--quiet",
|
|
134
|
+
action="store_true",
|
|
135
|
+
help="Suppress non-error output for cleaner automation",
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
parser.add_argument(
|
|
139
|
+
"--debug",
|
|
140
|
+
action="store_true",
|
|
141
|
+
help="Print debug output for detailed tracing",
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
parser.add_argument(
|
|
145
|
+
"--yes",
|
|
146
|
+
action="store_true",
|
|
147
|
+
help="Answer yes to all prompts",
|
|
148
|
+
default=False,
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
parser.add_argument(
|
|
152
|
+
"--config",
|
|
153
|
+
type=str,
|
|
154
|
+
help="A configuration setup like filepath or json string for a command",
|
|
155
|
+
default=None,
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
parser.add_argument(
|
|
159
|
+
"--timeout",
|
|
160
|
+
type=int,
|
|
161
|
+
help="Timeout for a command",
|
|
162
|
+
default=None,
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
parser.add_argument(
|
|
166
|
+
"--batch-size",
|
|
167
|
+
type=int,
|
|
168
|
+
default=None,
|
|
169
|
+
help="Number of items to process in each batch",
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
parser.add_argument(
|
|
173
|
+
"--no-input",
|
|
174
|
+
action="store_true",
|
|
175
|
+
help="Do not prompt for user input",
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
parser.add_argument(
|
|
179
|
+
"--threads",
|
|
180
|
+
type=int,
|
|
181
|
+
default=None,
|
|
182
|
+
help="Number of threads to use for processing",
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
parser.add_argument(
|
|
186
|
+
"--processes",
|
|
187
|
+
type=int,
|
|
188
|
+
default=None,
|
|
189
|
+
help="Number of processes to use for processing",
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
@abstractmethod
|
|
193
|
+
def add_command_arguments(self, parser: ArgumentParser) -> None:
|
|
194
|
+
"""Add command-specific arguments to the argument parser.
|
|
195
|
+
|
|
196
|
+
This abstract method must be implemented by subclasses to define
|
|
197
|
+
command-specific command-line arguments. It is called after common
|
|
198
|
+
base arguments are added, allowing each command to customize its
|
|
199
|
+
argument interface while maintaining consistent base functionality.
|
|
200
|
+
|
|
201
|
+
Subclasses should use this method to add arguments specific to their
|
|
202
|
+
command's functionality, such as file paths, configuration options,
|
|
203
|
+
or operational flags.
|
|
204
|
+
|
|
205
|
+
Args:
|
|
206
|
+
parser (ArgumentParser): Django's argument parser instance to which
|
|
207
|
+
command-specific arguments should be added.
|
|
208
|
+
|
|
209
|
+
Example:
|
|
210
|
+
>>> def add_command_arguments(self, parser):
|
|
211
|
+
... parser.add_argument(
|
|
212
|
+
... '--input-file',
|
|
213
|
+
... type=str,
|
|
214
|
+
... required=True,
|
|
215
|
+
... help='Path to input file'
|
|
216
|
+
... )
|
|
217
|
+
... parser.add_argument(
|
|
218
|
+
... '--output-format',
|
|
219
|
+
... choices=['json', 'csv', 'xml'],
|
|
220
|
+
... default='json',
|
|
221
|
+
... help='Output format for results'
|
|
222
|
+
... )
|
|
223
|
+
|
|
224
|
+
Note:
|
|
225
|
+
- This method is abstract and must be implemented by subclasses
|
|
226
|
+
- Called after _add_arguments() adds common base arguments
|
|
227
|
+
- Should focus on command-specific functionality only
|
|
228
|
+
"""
|
|
229
|
+
|
|
230
|
+
@final
|
|
231
|
+
def handle(self, *args: Any, **options: Any) -> None:
|
|
232
|
+
"""Execute the Django management command using template method pattern.
|
|
233
|
+
|
|
234
|
+
This method implements the main execution flow for the command by first
|
|
235
|
+
calling common handling logic through _handle(), then delegating to
|
|
236
|
+
the command-specific implementation via handle_command().
|
|
237
|
+
|
|
238
|
+
The @final decorator ensures this execution pattern cannot be overridden,
|
|
239
|
+
maintaining consistent command execution flow while allowing customization
|
|
240
|
+
through the abstract handle_command method.
|
|
241
|
+
|
|
242
|
+
Args:
|
|
243
|
+
*args: Positional arguments passed from Django's command execution.
|
|
244
|
+
**options: Keyword arguments containing parsed command-line options
|
|
245
|
+
and their values as defined by add_arguments().
|
|
246
|
+
|
|
247
|
+
Note:
|
|
248
|
+
- This method is final and cannot be overridden by subclasses
|
|
249
|
+
- Common handling logic is executed first via _handle()
|
|
250
|
+
- Command-specific logic is executed via abstract handle_command()
|
|
251
|
+
- All method calls are automatically logged with performance tracking
|
|
252
|
+
"""
|
|
253
|
+
self._handle(*args, **options)
|
|
254
|
+
self.handle_command(*args, **options)
|
|
255
|
+
|
|
256
|
+
@final
|
|
257
|
+
def _handle(self, *_args: Any, **options: Any) -> None:
|
|
258
|
+
"""Execute common handling logic shared across all commands.
|
|
259
|
+
|
|
260
|
+
This method is intended to contain common processing logic that should
|
|
261
|
+
be executed before command-specific handling. Currently, it serves as
|
|
262
|
+
a placeholder for future common functionality such as logging setup,
|
|
263
|
+
validation, or shared initialization.
|
|
264
|
+
|
|
265
|
+
The method is final to ensure consistent common handling across all
|
|
266
|
+
commands, while command-specific logic is handled through the abstract
|
|
267
|
+
handle_command method.
|
|
268
|
+
|
|
269
|
+
Args:
|
|
270
|
+
*args: Positional arguments passed from Django's command execution.
|
|
271
|
+
Currently unused but reserved for future common processing.
|
|
272
|
+
**options: Keyword arguments containing parsed command-line options.
|
|
273
|
+
Currently unused but reserved for future common processing.
|
|
274
|
+
|
|
275
|
+
Note:
|
|
276
|
+
- Examples might include logging setup, database connection validation, etc.
|
|
277
|
+
- The @final decorator prevents subclasses from overriding this method
|
|
278
|
+
- Called before handle_command() in the template method pattern
|
|
279
|
+
"""
|
|
280
|
+
# log each option for the command
|
|
281
|
+
for key, value in options.items():
|
|
282
|
+
logger.info(
|
|
283
|
+
"Command '%s' - runs with option: '%s' with value: '%s'",
|
|
284
|
+
self.__class__.__name__,
|
|
285
|
+
key,
|
|
286
|
+
value,
|
|
287
|
+
)
|
|
288
|
+
|
|
289
|
+
@abstractmethod
|
|
290
|
+
def handle_command(self, *args: Any, **options: Any) -> None:
|
|
291
|
+
"""Execute command-specific logic and functionality.
|
|
292
|
+
|
|
293
|
+
This abstract method must be implemented by subclasses to define the
|
|
294
|
+
core functionality of the Django management command. It is called after
|
|
295
|
+
common handling logic is executed, allowing each command to implement
|
|
296
|
+
its specific business logic while benefiting from shared infrastructure.
|
|
297
|
+
|
|
298
|
+
This method should contain the main logic that the command is designed
|
|
299
|
+
to perform, such as data processing, database operations, file manipulation,
|
|
300
|
+
or any other command-specific tasks.
|
|
301
|
+
|
|
302
|
+
Args:
|
|
303
|
+
*args: Positional arguments passed from Django's command execution.
|
|
304
|
+
These are typically not used in Django management commands.
|
|
305
|
+
**options: Keyword arguments containing parsed command-line options
|
|
306
|
+
and their values as defined by add_command_arguments().
|
|
307
|
+
|
|
308
|
+
Example:
|
|
309
|
+
>>> def handle_command(self, *args, **options):
|
|
310
|
+
... input_file = options['input_file']
|
|
311
|
+
... dry_run = options['dry_run'] # Base argument
|
|
312
|
+
... batch_size = options['batch_size'] # Base argument
|
|
313
|
+
... quiet = options['quiet'] # Base argument
|
|
314
|
+
...
|
|
315
|
+
... if dry_run:
|
|
316
|
+
... self.stdout.write('Dry run mode - no changes will be made')
|
|
317
|
+
...
|
|
318
|
+
... if not quiet:
|
|
319
|
+
... msg = f'Processing {input_file} in batches of {batch_size}'
|
|
320
|
+
... self.stdout.write(msg)
|
|
321
|
+
...
|
|
322
|
+
... # Perform command-specific operations
|
|
323
|
+
... self.process_file(input_file, batch_size, dry_run)
|
|
324
|
+
...
|
|
325
|
+
... if not quiet:
|
|
326
|
+
... self.stdout.write('Command completed successfully')
|
|
327
|
+
|
|
328
|
+
Note:
|
|
329
|
+
- This method is abstract and must be implemented by subclasses
|
|
330
|
+
- Called after _handle() executes common logic
|
|
331
|
+
- Should contain the main functionality of the command
|
|
332
|
+
- All method calls are automatically logged with performance tracking
|
|
333
|
+
- Use self.stdout.write() for output instead of print()
|
|
334
|
+
"""
|