0
# Output Rendering
1
2
The output rendering system provides flexible output formatting and presentation capabilities supporting multiple formats including JSON, tabular, and template-based output. It ensures consistent data presentation across different output handlers.
3
4
## Capabilities
5
6
### Output Handler Interface
7
8
Base interface for output rendering functionality that defines the contract for rendering operations.
9
10
```python { .api }
11
class OutputHandler:
12
"""
13
Output handler interface for rendering application data.
14
15
Provides methods for rendering data structures into formatted
16
output using various presentation formats and templates.
17
"""
18
19
def render(self, data: Dict[str, Any], template: str = None) -> str:
20
"""
21
Render data using the output handler.
22
23
Args:
24
data: Dictionary of data to render
25
template: Optional template name for template-based rendering
26
27
Returns:
28
Rendered output string
29
"""
30
```
31
32
## Usage Examples
33
34
### Basic Output Rendering
35
36
```python
37
from cement import App, Controller, ex
38
39
class BaseController(Controller):
40
class Meta:
41
label = 'base'
42
43
@ex(help='show user information')
44
def user_info(self):
45
"""Show user information with basic rendering."""
46
user_data = {
47
'id': 12345,
48
'name': 'John Doe',
49
'email': 'john@example.com',
50
'status': 'active',
51
'last_login': '2023-01-15T10:30:00Z',
52
'permissions': ['read', 'write', 'admin']
53
}
54
55
# Render using configured output handler
56
output = self.app.render(user_data)
57
print(output)
58
59
@ex(help='show system status')
60
def status(self):
61
"""Show system status information."""
62
status_data = {
63
'system': 'MyApp',
64
'version': '1.0.0',
65
'uptime': '5 days, 3 hours',
66
'memory_usage': '256MB',
67
'cpu_usage': '15%',
68
'active_users': 42,
69
'services': {
70
'database': 'online',
71
'cache': 'online',
72
'queue': 'online'
73
}
74
}
75
76
output = self.app.render(status_data)
77
print(output)
78
79
class MyApp(App):
80
class Meta:
81
label = 'myapp'
82
base_controller = 'base'
83
handlers = [BaseController]
84
85
with MyApp() as app:
86
app.run()
87
88
# Usage:
89
# myapp user-info
90
# myapp status
91
```
92
93
### JSON Output Handler
94
95
```python
96
from cement import App, Controller, ex, init_defaults
97
98
CONFIG = init_defaults('myapp')
99
CONFIG['output.json'] = {
100
'indent': 2,
101
'sort_keys': True
102
}
103
104
class DataController(Controller):
105
class Meta:
106
label = 'data'
107
stacked_on = 'base'
108
stacked_type = 'nested'
109
110
@ex(help='export data as JSON')
111
def export(self):
112
"""Export application data as JSON."""
113
export_data = {
114
'export_info': {
115
'timestamp': '2023-01-15T10:30:00Z',
116
'format': 'json',
117
'version': '1.0'
118
},
119
'users': [
120
{'id': 1, 'name': 'Alice', 'role': 'admin'},
121
{'id': 2, 'name': 'Bob', 'role': 'user'},
122
{'id': 3, 'name': 'Charlie', 'role': 'user'}
123
],
124
'settings': {
125
'theme': 'dark',
126
'notifications': True,
127
'auto_backup': False
128
}
129
}
130
131
# Render as JSON
132
json_output = self.app.render(export_data)
133
print(json_output)
134
135
class BaseController(Controller):
136
class Meta:
137
label = 'base'
138
139
class MyApp(App):
140
class Meta:
141
label = 'myapp'
142
base_controller = 'base'
143
extensions = ['json']
144
output_handler = 'json'
145
config_defaults = CONFIG
146
handlers = [BaseController, DataController]
147
148
with MyApp() as app:
149
app.run()
150
151
# Usage:
152
# myapp data export
153
```
154
155
### Tabular Output Handler
156
157
```python
158
from cement import App, Controller, ex, init_defaults
159
160
CONFIG = init_defaults('myapp')
161
CONFIG['output.tabulate'] = {
162
'tablefmt': 'grid', # grid, simple, plain, html, etc.
163
'headers': 'keys'
164
}
165
166
class ReportController(Controller):
167
class Meta:
168
label = 'report'
169
stacked_on = 'base'
170
stacked_type = 'nested'
171
172
@ex(help='show user report')
173
def users(self):
174
"""Show user report in tabular format."""
175
# Data structured for tabular output
176
user_report = {
177
'users': [
178
{'ID': 1, 'Name': 'Alice Johnson', 'Email': 'alice@example.com', 'Status': 'Active'},
179
{'ID': 2, 'Name': 'Bob Smith', 'Email': 'bob@example.com', 'Status': 'Inactive'},
180
{'ID': 3, 'Name': 'Charlie Brown', 'Email': 'charlie@example.com', 'Status': 'Active'},
181
{'ID': 4, 'Name': 'Diana Prince', 'Email': 'diana@example.com', 'Status': 'Active'}
182
]
183
}
184
185
# Render as table
186
table_output = self.app.render(user_report)
187
print(table_output)
188
189
@ex(help='show system metrics')
190
def metrics(self):
191
"""Show system metrics in tabular format."""
192
metrics_data = {
193
'metrics': [
194
{'Metric': 'CPU Usage', 'Value': '15%', 'Threshold': '80%', 'Status': 'OK'},
195
{'Metric': 'Memory Usage', 'Value': '256MB', 'Threshold': '1GB', 'Status': 'OK'},
196
{'Metric': 'Disk Usage', 'Value': '45GB', 'Threshold': '100GB', 'Status': 'Warning'},
197
{'Metric': 'Active Users', 'Value': '42', 'Threshold': '100', 'Status': 'OK'}
198
]
199
}
200
201
output = self.app.render(metrics_data)
202
print(output)
203
204
class BaseController(Controller):
205
class Meta:
206
label = 'base'
207
208
class MyApp(App):
209
class Meta:
210
label = 'myapp'
211
base_controller = 'base'
212
extensions = ['tabulate']
213
output_handler = 'tabulate'
214
config_defaults = CONFIG
215
handlers = [BaseController, ReportController]
216
217
with MyApp() as app:
218
app.run()
219
220
# Usage:
221
# myapp report users
222
# myapp report metrics
223
```
224
225
### Template-Based Output
226
227
```python
228
from cement import App, Controller, ex, init_defaults
229
230
CONFIG = init_defaults('myapp')
231
CONFIG['template.jinja2'] = {
232
'template_dirs': ['./templates']
233
}
234
235
class TemplateController(Controller):
236
class Meta:
237
label = 'template'
238
stacked_on = 'base'
239
stacked_type = 'nested'
240
241
@ex(help='generate user report')
242
def user_report(self):
243
"""Generate user report using template."""
244
report_data = {
245
'title': 'User Activity Report',
246
'generated_at': '2023-01-15 10:30:00',
247
'users': [
248
{
249
'name': 'Alice Johnson',
250
'email': 'alice@example.com',
251
'last_login': '2023-01-14',
252
'actions': 25,
253
'status': 'active'
254
},
255
{
256
'name': 'Bob Smith',
257
'email': 'bob@example.com',
258
'last_login': '2023-01-10',
259
'actions': 12,
260
'status': 'inactive'
261
}
262
],
263
'summary': {
264
'total_users': 2,
265
'active_users': 1,
266
'total_actions': 37
267
}
268
}
269
270
# Render using template
271
output = self.app.render(report_data, template='user_report.txt')
272
print(output)
273
274
@ex(help='generate system summary')
275
def system_summary(self):
276
"""Generate system summary using template."""
277
system_data = {
278
'system_name': 'MyApp Production',
279
'version': '1.0.0',
280
'environment': 'production',
281
'services': [
282
{'name': 'Web Server', 'status': 'running', 'uptime': '5d 3h'},
283
{'name': 'Database', 'status': 'running', 'uptime': '10d 2h'},
284
{'name': 'Cache', 'status': 'running', 'uptime': '5d 3h'},
285
{'name': 'Queue', 'status': 'running', 'uptime': '3d 1h'}
286
],
287
'alerts': [
288
{'level': 'warning', 'message': 'Disk usage above 80%'},
289
{'level': 'info', 'message': 'Scheduled maintenance in 2 days'}
290
]
291
}
292
293
output = self.app.render(system_data, template='system_summary.txt')
294
print(output)
295
296
class BaseController(Controller):
297
class Meta:
298
label = 'base'
299
300
class MyApp(App):
301
class Meta:
302
label = 'myapp'
303
base_controller = 'base'
304
extensions = ['jinja2']
305
template_handler = 'jinja2'
306
output_handler = 'jinja2'
307
config_defaults = CONFIG
308
handlers = [BaseController, TemplateController]
309
310
with MyApp() as app:
311
app.run()
312
313
# Usage:
314
# myapp template user-report
315
# myapp template system-summary
316
317
# Example template file: templates/user_report.txt
318
"""
319
{{ title }}
320
Generated: {{ generated_at }}
321
322
User Summary:
323
- Total Users: {{ summary.total_users }}
324
- Active Users: {{ summary.active_users }}
325
- Total Actions: {{ summary.total_actions }}
326
327
User Details:
328
{% for user in users %}
329
- {{ user.name }} ({{ user.email }})
330
Last Login: {{ user.last_login }}
331
Actions: {{ user.actions }}
332
Status: {{ user.status }}
333
{% endfor %}
334
"""
335
```
336
337
### Dynamic Output Handler Selection
338
339
```python
340
from cement import App, Controller, ex
341
342
class OutputController(Controller):
343
class Meta:
344
label = 'output'
345
stacked_on = 'base'
346
stacked_type = 'nested'
347
arguments = [
348
(['--format'], {
349
'choices': ['json', 'table', 'yaml'],
350
'default': 'json',
351
'help': 'output format'
352
})
353
]
354
355
@ex(help='show data in different formats')
356
def show(self):
357
"""Show data in user-specified format."""
358
data = {
359
'application': 'MyApp',
360
'users': [
361
{'id': 1, 'name': 'Alice', 'active': True},
362
{'id': 2, 'name': 'Bob', 'active': False}
363
],
364
'stats': {
365
'total_users': 2,
366
'active_users': 1
367
}
368
}
369
370
# Get desired format from arguments
371
output_format = self.app.pargs.format
372
373
# Temporarily switch output handler
374
original_handler = self.app._meta.output_handler
375
376
if output_format == 'json':
377
self.app._setup_output_handler('json')
378
elif output_format == 'table':
379
self.app._setup_output_handler('tabulate')
380
elif output_format == 'yaml':
381
self.app._setup_output_handler('yaml')
382
383
# Render with selected handler
384
output = self.app.render(data)
385
print(output)
386
387
# Restore original handler
388
self.app._setup_output_handler(original_handler)
389
390
class BaseController(Controller):
391
class Meta:
392
label = 'base'
393
394
class MyApp(App):
395
class Meta:
396
label = 'myapp'
397
base_controller = 'base'
398
extensions = ['json', 'tabulate', 'yaml']
399
handlers = [BaseController, OutputController]
400
401
with MyApp() as app:
402
app.run()
403
404
# Usage:
405
# myapp output show --format json
406
# myapp output show --format table
407
# myapp output show --format yaml
408
```
409
410
### Custom Output Processing
411
412
```python
413
from cement import App, Controller, ex
414
import csv
415
import io
416
417
class CsvController(Controller):
418
class Meta:
419
label = 'csv'
420
stacked_on = 'base'
421
stacked_type = 'nested'
422
423
def render_csv(self, data):
424
"""Custom CSV rendering method."""
425
if not isinstance(data, dict) or 'rows' not in data:
426
raise ValueError("CSV data must contain 'rows' key")
427
428
output = io.StringIO()
429
430
# Get headers from first row or use provided headers
431
rows = data['rows']
432
if not rows:
433
return ''
434
435
headers = data.get('headers', list(rows[0].keys()))
436
437
writer = csv.DictWriter(output, fieldnames=headers)
438
writer.writeheader()
439
440
for row in rows:
441
writer.writerow(row)
442
443
return output.getvalue()
444
445
@ex(help='export data as CSV')
446
def export(self):
447
"""Export data in CSV format."""
448
csv_data = {
449
'headers': ['ID', 'Name', 'Email', 'Status'],
450
'rows': [
451
{'ID': 1, 'Name': 'Alice Johnson', 'Email': 'alice@example.com', 'Status': 'Active'},
452
{'ID': 2, 'Name': 'Bob Smith', 'Email': 'bob@example.com', 'Status': 'Inactive'},
453
{'ID': 3, 'Name': 'Charlie Brown', 'Email': 'charlie@example.com', 'Status': 'Active'}
454
]
455
}
456
457
# Use custom CSV rendering
458
csv_output = self.render_csv(csv_data)
459
print(csv_output)
460
461
@ex(
462
help='save data to CSV file',
463
arguments=[
464
(['--output', '-o'], {
465
'help': 'output file path',
466
'default': 'data.csv'
467
})
468
]
469
)
470
def save(self):
471
"""Save data to CSV file."""
472
csv_data = {
473
'rows': [
474
{'Product': 'Widget A', 'Price': 19.99, 'Stock': 100},
475
{'Product': 'Widget B', 'Price': 29.99, 'Stock': 50},
476
{'Product': 'Widget C', 'Price': 39.99, 'Stock': 25}
477
]
478
}
479
480
csv_output = self.render_csv(csv_data)
481
482
output_file = self.app.pargs.output
483
with open(output_file, 'w') as f:
484
f.write(csv_output)
485
486
print(f'Data saved to {output_file}')
487
488
class BaseController(Controller):
489
class Meta:
490
label = 'base'
491
492
class MyApp(App):
493
class Meta:
494
label = 'myapp'
495
base_controller = 'base'
496
handlers = [BaseController, CsvController]
497
498
with MyApp() as app:
499
app.run()
500
501
# Usage:
502
# myapp csv export
503
# myapp csv save --output users.csv
504
```