0
# Argument Parsing
1
2
The argument parsing system provides command-line argument processing built on Python's argparse module. It offers unified argument handling that integrates with configuration management and controller systems.
3
4
## Capabilities
5
6
### Argument Handler Interface
7
8
Base interface for argument parsing functionality that defines the contract for command-line argument operations.
9
10
```python { .api }
11
class ArgumentHandler:
12
"""
13
Argument handler interface for parsing command-line arguments.
14
15
Provides methods for adding arguments, parsing command-line input,
16
and integrating with the application configuration system.
17
"""
18
19
def add_argument(self, *args: Any, **kwargs: Any) -> None:
20
"""
21
Add a command-line argument.
22
23
Args:
24
*args: Positional arguments (e.g., '--verbose', '-v')
25
**kwargs: Keyword arguments (help, action, type, etc.)
26
"""
27
28
def parse(self, argv: List[str]) -> Any:
29
"""
30
Parse command-line arguments.
31
32
Args:
33
argv: List of command-line arguments to parse
34
35
Returns:
36
Parsed arguments object
37
"""
38
```
39
40
## Usage Examples
41
42
### Basic Argument Parsing
43
44
```python
45
from cement import App, Controller, ex
46
47
class BaseController(Controller):
48
class Meta:
49
label = 'base'
50
arguments = [
51
(['--verbose', '-v'], {
52
'help': 'verbose output',
53
'action': 'store_true'
54
}),
55
(['--config'], {
56
'help': 'configuration file path',
57
'dest': 'config_file'
58
})
59
]
60
61
def _default(self):
62
"""Default action when no sub-command specified."""
63
if self.app.pargs.verbose:
64
print('Verbose mode enabled')
65
66
if hasattr(self.app.pargs, 'config_file') and self.app.pargs.config_file:
67
print(f'Using config: {self.app.pargs.config_file}')
68
69
class MyApp(App):
70
class Meta:
71
label = 'myapp'
72
base_controller = 'base'
73
handlers = [BaseController]
74
75
with MyApp() as app:
76
app.run()
77
78
# Usage:
79
# myapp --verbose
80
# myapp --config /path/to/config.conf
81
```
82
83
### Command-Specific Arguments
84
85
```python
86
from cement import App, Controller, ex
87
88
class FileController(Controller):
89
class Meta:
90
label = 'file'
91
stacked_on = 'base'
92
stacked_type = 'nested'
93
94
@ex(
95
help='process files',
96
arguments=[
97
(['files'], {
98
'nargs': '+',
99
'help': 'files to process'
100
}),
101
(['--output', '-o'], {
102
'help': 'output directory',
103
'default': './output'
104
}),
105
(['--format'], {
106
'choices': ['json', 'yaml', 'xml'],
107
'default': 'json',
108
'help': 'output format'
109
}),
110
(['--recursive', '-r'], {
111
'action': 'store_true',
112
'help': 'process directories recursively'
113
})
114
]
115
)
116
def process(self):
117
"""Process files with various options."""
118
files = self.app.pargs.files
119
output_dir = self.app.pargs.output
120
format_type = self.app.pargs.format
121
recursive = self.app.pargs.recursive
122
123
print(f'Processing {len(files)} files')
124
print(f'Output directory: {output_dir}')
125
print(f'Format: {format_type}')
126
print(f'Recursive: {recursive}')
127
128
for file_path in files:
129
print(f'Processing: {file_path}')
130
131
class BaseController(Controller):
132
class Meta:
133
label = 'base'
134
135
class MyApp(App):
136
class Meta:
137
label = 'myapp'
138
base_controller = 'base'
139
handlers = [BaseController, FileController]
140
141
with MyApp() as app:
142
app.run()
143
144
# Usage:
145
# myapp file process file1.txt file2.txt --format yaml --recursive
146
```
147
148
### Global vs Local Arguments
149
150
```python
151
from cement import App, Controller, ex
152
153
class BaseController(Controller):
154
class Meta:
155
label = 'base'
156
# Global arguments available to all commands
157
arguments = [
158
(['--debug'], {
159
'action': 'store_true',
160
'help': 'enable debug mode'
161
}),
162
(['--log-level'], {
163
'choices': ['DEBUG', 'INFO', 'WARNING', 'ERROR'],
164
'default': 'INFO',
165
'help': 'set logging level'
166
})
167
]
168
169
@ex(
170
help='show application status',
171
# Command-specific arguments
172
arguments=[
173
(['--detailed'], {
174
'action': 'store_true',
175
'help': 'show detailed status'
176
})
177
]
178
)
179
def status(self):
180
"""Show application status."""
181
# Access global arguments
182
debug = self.app.pargs.debug
183
log_level = self.app.pargs.log_level
184
185
# Access command-specific arguments
186
detailed = self.app.pargs.detailed
187
188
print(f'Debug mode: {debug}')
189
print(f'Log level: {log_level}')
190
191
if detailed:
192
print('Detailed status information...')
193
194
class MyApp(App):
195
class Meta:
196
label = 'myapp'
197
base_controller = 'base'
198
handlers = [BaseController]
199
200
with MyApp() as app:
201
app.run()
202
203
# Usage:
204
# myapp --debug --log-level DEBUG status --detailed
205
```
206
207
### Argument Validation
208
209
```python
210
from cement import App, Controller, ex
211
import os
212
213
def validate_file_exists(value):
214
"""Custom argument type that validates file exists."""
215
if not os.path.exists(value):
216
raise argparse.ArgumentTypeError(f"File '{value}' does not exist")
217
return value
218
219
def validate_positive_int(value):
220
"""Custom argument type that validates positive integers."""
221
try:
222
ivalue = int(value)
223
if ivalue <= 0:
224
raise argparse.ArgumentTypeError(f"'{value}' must be a positive integer")
225
return ivalue
226
except ValueError:
227
raise argparse.ArgumentTypeError(f"'{value}' is not a valid integer")
228
229
class BaseController(Controller):
230
class Meta:
231
label = 'base'
232
233
@ex(
234
help='backup file',
235
arguments=[
236
(['source'], {
237
'type': validate_file_exists,
238
'help': 'source file to backup'
239
}),
240
(['--destination'], {
241
'help': 'backup destination path'
242
}),
243
(['--max-backups'], {
244
'type': validate_positive_int,
245
'default': 5,
246
'help': 'maximum number of backups to keep'
247
})
248
]
249
)
250
def backup(self):
251
"""Backup a file with validation."""
252
source = self.app.pargs.source
253
destination = self.app.pargs.destination or f"{source}.bak"
254
max_backups = self.app.pargs.max_backups
255
256
print(f'Backing up {source} to {destination}')
257
print(f'Keeping maximum {max_backups} backups')
258
259
class MyApp(App):
260
class Meta:
261
label = 'myapp'
262
base_controller = 'base'
263
handlers = [BaseController]
264
265
with MyApp() as app:
266
app.run()
267
268
# Usage:
269
# myapp backup /path/to/file.txt --max-backups 10
270
```
271
272
### Environment Variable Integration
273
274
```python
275
from cement import App, Controller, ex
276
import os
277
278
class BaseController(Controller):
279
class Meta:
280
label = 'base'
281
arguments = [
282
(['--database-url'], {
283
'help': 'database connection URL',
284
'default': os.getenv('DATABASE_URL', 'sqlite:///app.db'),
285
'dest': 'database_url'
286
}),
287
(['--api-key'], {
288
'help': 'API key for external service',
289
'default': os.getenv('API_KEY'),
290
'dest': 'api_key'
291
}),
292
(['--workers'], {
293
'type': int,
294
'help': 'number of worker processes',
295
'default': int(os.getenv('WORKERS', '4')),
296
'dest': 'worker_count'
297
})
298
]
299
300
@ex(help='show configuration')
301
def config(self):
302
"""Show current configuration from args/env."""
303
print(f'Database URL: {self.app.pargs.database_url}')
304
print(f'API Key: {"***" if self.app.pargs.api_key else "Not set"}')
305
print(f'Workers: {self.app.pargs.worker_count}')
306
307
class MyApp(App):
308
class Meta:
309
label = 'myapp'
310
base_controller = 'base'
311
handlers = [BaseController]
312
313
with MyApp() as app:
314
app.run()
315
316
# Environment variables take precedence:
317
# DATABASE_URL=postgresql://... myapp config
318
# myapp config --database-url mysql://...
319
```
320
321
### Complex Argument Patterns
322
323
```python
324
from cement import App, Controller, ex
325
import argparse
326
327
class DeployController(Controller):
328
class Meta:
329
label = 'deploy'
330
stacked_on = 'base'
331
stacked_type = 'nested'
332
333
@ex(
334
help='deploy application',
335
arguments=[
336
(['environment'], {
337
'choices': ['development', 'staging', 'production'],
338
'help': 'deployment environment'
339
}),
340
(['--version'], {
341
'help': 'application version to deploy'
342
}),
343
(['--config-file'], {
344
'action': 'append',
345
'help': 'configuration files (can be specified multiple times)'
346
}),
347
(['--dry-run'], {
348
'action': 'store_true',
349
'help': 'show what would be deployed without actually deploying'
350
}),
351
(['--force'], {
352
'action': 'store_true',
353
'help': 'force deployment even if validation fails'
354
}),
355
(['--exclude'], {
356
'action': 'append',
357
'help': 'exclude specific components from deployment'
358
}),
359
(['--timeout'], {
360
'type': int,
361
'default': 300,
362
'help': 'deployment timeout in seconds'
363
})
364
]
365
)
366
def start(self):
367
"""Start deployment process."""
368
env = self.app.pargs.environment
369
version = self.app.pargs.version or 'latest'
370
config_files = self.app.pargs.config_file or []
371
dry_run = self.app.pargs.dry_run
372
force = self.app.pargs.force
373
excludes = self.app.pargs.exclude or []
374
timeout = self.app.pargs.timeout
375
376
print(f'Deploying to {env} environment')
377
print(f'Version: {version}')
378
print(f'Config files: {config_files}')
379
print(f'Dry run: {dry_run}')
380
print(f'Force: {force}')
381
print(f'Excludes: {excludes}')
382
print(f'Timeout: {timeout}s')
383
384
class BaseController(Controller):
385
class Meta:
386
label = 'base'
387
388
class MyApp(App):
389
class Meta:
390
label = 'myapp'
391
base_controller = 'base'
392
handlers = [BaseController, DeployController]
393
394
with MyApp() as app:
395
app.run()
396
397
# Usage:
398
# myapp deploy production --version 1.2.3 --config-file prod.conf --config-file secrets.conf --exclude monitoring --dry-run
399
```