or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

arguments.mdcaching.mdconfiguration.mdcontrollers.mdextensions.mdfoundation.mdhooks.mdindex.mdinterface-handler.mdlogging.mdmail.mdoutput.mdplugins.mdtemplates.mdutilities.md

controllers.mddocs/

0

# Controllers and Commands

1

2

The controller system provides command organization and sub-command functionality using an argparse-based framework. Controllers enable organizing CLI functionality into logical groups with nested commands and parameter handling.

3

4

## Capabilities

5

6

### Controller Base Class

7

8

Base controller class that all application controllers should inherit from. Provides the foundation for command organization and argument handling.

9

10

```python { .api }

11

class Controller:

12

"""

13

Base controller class for organizing CLI commands.

14

15

Controllers group related commands together and provide a structured

16

way to organize application functionality. Each controller can have

17

multiple commands exposed through decorated methods.

18

"""

19

20

def __init__(self, **kw: Any) -> None:

21

"""

22

Initialize the controller.

23

24

Args:

25

**kw: Additional keyword arguments

26

"""

27

28

@property

29

def _meta(self) -> Any:

30

"""Controller meta-data configuration object."""

31

```

32

33

### Expose Decorator

34

35

Decorator for exposing controller methods as CLI commands. The exposed methods become available as sub-commands when the application runs.

36

37

```python { .api }

38

def expose(

39

hide: bool = False,

40

arguments: List[ArgparseArgumentType] = [],

41

label: str = None,

42

**parser_options: Any

43

) -> Callable:

44

"""

45

Decorator that exposes controller methods as CLI commands.

46

47

Args:

48

hide: Whether to hide the command from help output

49

arguments: List of argparse argument definitions

50

label: Override command name (defaults to method name)

51

**parser_options: Additional argparse subparser options (help, aliases, etc.)

52

53

Returns:

54

Decorated function that becomes a CLI command

55

"""

56

57

# Alias for expose decorator

58

ex = expose

59

```

60

61

### Controller Meta Configuration

62

63

Controller behavior is controlled through the Meta class which defines controller properties and command handling options.

64

65

```python { .api }

66

class Meta:

67

"""

68

Controller meta-data configuration.

69

70

Controls controller behavior including label, stacked commands,

71

and argument handling.

72

"""

73

74

label: str = None

75

"""Controller label/name"""

76

77

stacked_on: str = None

78

"""Stack this controller on top of another controller"""

79

80

stacked_type: str = 'embedded'

81

"""Stacking type: 'embedded' or 'nested'"""

82

83

description: str = None

84

"""Controller description for help text"""

85

86

usage: str = None

87

"""Custom usage string for help text"""

88

89

help: str = None

90

"""Help text for the controller"""

91

92

aliases: List[str] = []

93

"""List of controller aliases"""

94

95

arguments: List[Tuple[List[str], Dict[str, Any]]] = []

96

"""List of controller-level arguments"""

97

```

98

99

## Usage Examples

100

101

### Basic Controller

102

103

```python

104

from cement import App, Controller, ex

105

106

class BaseController(Controller):

107

class Meta:

108

label = 'base'

109

description = 'Base controller for application commands'

110

111

@ex(help='display application information')

112

def info(self):

113

"""Display application information."""

114

print('MyApp v1.0.0')

115

print('A sample cement application')

116

117

@ex(

118

help='greet someone',

119

arguments=[

120

(['name'], {'help': 'name to greet'}),

121

(['--uppercase', '-u'], {

122

'help': 'print in uppercase',

123

'action': 'store_true'

124

})

125

]

126

)

127

def greet(self):

128

"""Greet someone by name."""

129

name = self.app.pargs.name

130

greeting = f'Hello {name}!'

131

132

if self.app.pargs.uppercase:

133

greeting = greeting.upper()

134

135

print(greeting)

136

137

class MyApp(App):

138

class Meta:

139

label = 'myapp'

140

base_controller = 'base'

141

handlers = [BaseController]

142

143

with MyApp() as app:

144

app.run()

145

```

146

147

### Stacked Controllers

148

149

```python

150

from cement import App, Controller, ex

151

152

class BaseController(Controller):

153

class Meta:

154

label = 'base'

155

156

@ex(help='base command')

157

def default(self):

158

print('Base controller default command')

159

160

class DatabaseController(Controller):

161

class Meta:

162

label = 'database'

163

stacked_on = 'base'

164

stacked_type = 'nested'

165

description = 'Database management commands'

166

167

@ex(help='initialize database')

168

def init(self):

169

print('Initializing database...')

170

171

@ex(help='migrate database')

172

def migrate(self):

173

print('Running migrations...')

174

175

class UserController(Controller):

176

class Meta:

177

label = 'user'

178

stacked_on = 'base'

179

stacked_type = 'nested'

180

description = 'User management commands'

181

182

@ex(

183

help='create a new user',

184

arguments=[

185

(['username'], {'help': 'username for new user'}),

186

(['--email'], {'help': 'email address'})

187

]

188

)

189

def create(self):

190

username = self.app.pargs.username

191

email = getattr(self.app.pargs, 'email', None)

192

print(f'Creating user: {username}')

193

if email:

194

print(f'Email: {email}')

195

196

class MyApp(App):

197

class Meta:

198

label = 'myapp'

199

base_controller = 'base'

200

handlers = [

201

BaseController,

202

DatabaseController,

203

UserController

204

]

205

206

with MyApp() as app:

207

app.run()

208

209

# Usage:

210

# myapp info # Base controller

211

# myapp database init # Database controller

212

# myapp database migrate # Database controller

213

# myapp user create john # User controller

214

# myapp user create jane --email jane@example.com

215

```

216

217

### Controller with Arguments

218

219

```python

220

from cement import App, Controller, ex

221

222

class BaseController(Controller):

223

class Meta:

224

label = 'base'

225

arguments = [

226

(['--verbose', '-v'], {

227

'help': 'verbose output',

228

'action': 'store_true'

229

}),

230

(['--config'], {

231

'help': 'configuration file path',

232

'dest': 'config_file'

233

})

234

]

235

236

def _default(self):

237

"""Default command when no sub-command is specified."""

238

if self.app.pargs.verbose:

239

print('Verbose mode enabled')

240

241

if hasattr(self.app.pargs, 'config_file') and self.app.pargs.config_file:

242

print(f'Using config file: {self.app.pargs.config_file}')

243

244

print('Default command executed')

245

246

@ex(

247

help='process files',

248

arguments=[

249

(['files'], {

250

'nargs': '+',

251

'help': 'files to process'

252

}),

253

(['--output', '-o'], {

254

'help': 'output directory',

255

'default': './output'

256

}),

257

(['--format'], {

258

'choices': ['json', 'yaml', 'xml'],

259

'default': 'json',

260

'help': 'output format'

261

})

262

]

263

)

264

def process(self):

265

"""Process multiple files."""

266

files = self.app.pargs.files

267

output_dir = self.app.pargs.output

268

format_type = self.app.pargs.format

269

270

print(f'Processing {len(files)} files')

271

print(f'Output directory: {output_dir}')

272

print(f'Output format: {format_type}')

273

274

for file_path in files:

275

print(f'Processing: {file_path}')

276

277

class MyApp(App):

278

class Meta:

279

label = 'myapp'

280

base_controller = 'base'

281

handlers = [BaseController]

282

283

with MyApp() as app:

284

app.run()

285

286

# Usage:

287

# myapp --verbose

288

# myapp --config /etc/myapp.conf

289

# myapp process file1.txt file2.txt --output /tmp --format yaml

290

```

291

292

### Command Aliases

293

294

```python

295

from cement import App, Controller, ex

296

297

class BaseController(Controller):

298

class Meta:

299

label = 'base'

300

301

@ex(

302

help='display status information',

303

aliases=['stat', 'st']

304

)

305

def status(self):

306

"""Display application status."""

307

print('Application is running')

308

309

@ex(

310

help='restart the application',

311

aliases=['reboot', 'reload']

312

)

313

def restart(self):

314

"""Restart the application."""

315

print('Restarting application...')

316

317

class MyApp(App):

318

class Meta:

319

label = 'myapp'

320

base_controller = 'base'

321

handlers = [BaseController]

322

323

with MyApp() as app:

324

app.run()

325

326

# All of these commands are equivalent:

327

# myapp status

328

# myapp stat

329

# myapp st

330

```

331

332

### Hidden Commands

333

334

```python

335

from cement import App, Controller, ex

336

337

class BaseController(Controller):

338

class Meta:

339

label = 'base'

340

341

@ex(help='public command')

342

def public_cmd(self):

343

"""This command appears in help."""

344

print('Public command executed')

345

346

@ex(

347

help='hidden command for debugging',

348

hide=True

349

)

350

def debug_cmd(self):

351

"""This command is hidden from help but still callable."""

352

print('Debug command executed')

353

354

class MyApp(App):

355

class Meta:

356

label = 'myapp'

357

base_controller = 'base'

358

handlers = [BaseController]

359

360

with MyApp() as app:

361

app.run()

362

363

# myapp debug_cmd still works, but won't appear in --help

364

```

365

366

### Accessing Application Context

367

368

```python

369

from cement import App, Controller, ex

370

371

class BaseController(Controller):

372

class Meta:

373

label = 'base'

374

375

@ex(help='demonstrate app context access')

376

def demo(self):

377

"""Demonstrate accessing application context."""

378

# Access application configuration

379

debug_mode = self.app.config.get('myapp', 'debug')

380

print(f'Debug mode: {debug_mode}')

381

382

# Access logging

383

self.app.log.info('Demo command executed')

384

385

# Access parsed arguments

386

if hasattr(self.app.pargs, 'verbose'):

387

print(f'Verbose: {self.app.pargs.verbose}')

388

389

# Render output using output handler

390

data = {'message': 'Hello from demo command', 'timestamp': '2023-01-01'}

391

output = self.app.render(data)

392

print(output)

393

394

class MyApp(App):

395

class Meta:

396

label = 'myapp'

397

base_controller = 'base'

398

handlers = [BaseController]

399

400

with MyApp() as app:

401

app.run()

402

```