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

interface-handler.mddocs/

0

# Interface and Handler System

1

2

The interface and handler system provides the foundation for cement's pluggable architecture. Interfaces define contracts for functionality while handlers provide concrete implementations, enabling customizable behavior across all framework components.

3

4

## Capabilities

5

6

### Interface Base Class

7

8

Base class for defining framework interfaces that specify contracts for functionality.

9

10

```python { .api }

11

class Interface:

12

"""

13

Base interface class that all cement interfaces should inherit from.

14

15

Interfaces define contracts and specifications for functionality

16

that handlers must implement. They provide the blueprint for

17

pluggable components in the cement framework.

18

"""

19

20

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

21

"""

22

Initialize the interface.

23

24

Args:

25

**kw: Additional keyword arguments

26

"""

27

28

def _validate(self) -> None:

29

"""

30

Perform validation to ensure proper interface definition.

31

32

This method should be overridden by interface implementations

33

to validate interface-specific requirements.

34

"""

35

36

@property

37

def _meta(self) -> Any:

38

"""Interface meta-data configuration object."""

39

```

40

41

### Handler Base Class

42

43

Base class for implementing interface contracts through concrete handler implementations.

44

45

```python { .api }

46

class Handler:

47

"""

48

Base handler class that all cement handlers should inherit from.

49

50

Handlers provide concrete implementations of interface contracts.

51

They contain the actual functionality that interfaces define.

52

"""

53

54

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

55

"""

56

Initialize the handler.

57

58

Args:

59

**kw: Additional keyword arguments

60

"""

61

62

def _validate(self) -> None:

63

"""

64

Perform validation to ensure proper handler implementation.

65

66

This method should be overridden by handler implementations

67

to validate handler-specific requirements and interface compliance.

68

"""

69

70

@property

71

def _meta(self) -> Any:

72

"""Handler meta-data configuration object."""

73

```

74

75

### Interface Manager

76

77

Manages the interface system including interface definition, registration, and retrieval.

78

79

```python { .api }

80

class InterfaceManager:

81

"""

82

Manages the interface system for defining and retrieving interfaces.

83

84

The interface manager provides centralized control over interface

85

registration and lookup functionality.

86

"""

87

88

def define(self, interface: Interface) -> None:

89

"""

90

Define a new interface in the system.

91

92

Args:

93

interface: Interface class to define

94

"""

95

96

def defined(self, interface: str) -> bool:

97

"""

98

Check if an interface is defined.

99

100

Args:

101

interface: Interface name to check

102

103

Returns:

104

True if interface is defined, False otherwise

105

"""

106

107

def list(self) -> List[str]:

108

"""

109

Get list of all defined interfaces.

110

111

Returns:

112

List of interface names

113

"""

114

115

def get(self, interface: str) -> Interface:

116

"""

117

Get an interface by name.

118

119

Args:

120

interface: Interface name to retrieve

121

122

Returns:

123

Interface class

124

125

Raises:

126

InterfaceError: If interface is not found

127

"""

128

```

129

130

### Handler Manager

131

132

Manages the handler system including handler registration, resolution, and retrieval.

133

134

```python { .api }

135

class HandlerManager:

136

"""

137

Manages the handler system for registering and resolving handlers.

138

139

The handler manager provides centralized control over handler

140

registration, lookup, and resolution functionality.

141

"""

142

143

def register(self, handler: Handler) -> None:

144

"""

145

Register a handler in the system.

146

147

Args:

148

handler: Handler class to register

149

"""

150

151

def registered(self, interface: str, handler: str) -> bool:

152

"""

153

Check if a handler is registered for an interface.

154

155

Args:

156

interface: Interface name

157

handler: Handler name

158

159

Returns:

160

True if handler is registered, False otherwise

161

"""

162

163

def list(self, interface: str) -> List[str]:

164

"""

165

Get list of registered handlers for an interface.

166

167

Args:

168

interface: Interface name

169

170

Returns:

171

List of handler names for the interface

172

"""

173

174

def get(self, interface: str, handler: str) -> Handler:

175

"""

176

Get a handler instance by interface and handler name.

177

178

Args:

179

interface: Interface name

180

handler: Handler name

181

182

Returns:

183

Handler instance

184

185

Raises:

186

FrameworkError: If handler is not found

187

"""

188

189

def resolve(self, interface: str, handler: str) -> Handler:

190

"""

191

Resolve and return a handler instance.

192

193

Args:

194

interface: Interface name

195

handler: Handler name

196

197

Returns:

198

Resolved handler instance

199

200

Raises:

201

FrameworkError: If handler cannot be resolved

202

"""

203

```

204

205

### Interface Meta Configuration

206

207

Interface behavior is controlled through the Meta class that defines interface properties.

208

209

```python { .api }

210

class Meta:

211

"""

212

Interface meta-data configuration.

213

214

Controls interface behavior and properties.

215

"""

216

217

interface: str = None

218

"""The string identifier of this interface"""

219

```

220

221

### Handler Meta Configuration

222

223

Handler behavior is controlled through the Meta class that defines handler properties and interface association.

224

225

```python { .api }

226

class Meta:

227

"""

228

Handler meta-data configuration.

229

230

Controls handler behavior, interface association, and configuration.

231

"""

232

233

label: str = None

234

"""The string identifier of this handler"""

235

236

interface: str = None

237

"""The interface that this handler implements"""

238

239

config_section: str = None

240

"""Configuration section to merge config_defaults with"""

241

242

config_defaults: Dict[str, Any] = None

243

"""Default configuration dictionary"""

244

245

overridable: bool = False

246

"""Whether this handler can be overridden"""

247

```

248

249

## Usage Examples

250

251

### Defining a Custom Interface

252

253

```python

254

from cement import Interface

255

256

class DatabaseInterface(Interface):

257

"""

258

Interface for database operations.

259

260

Defines the contract that database handlers must implement.

261

"""

262

263

class Meta:

264

interface = 'database'

265

266

def connect(self, **kwargs):

267

"""

268

Connect to database.

269

270

This method must be implemented by handlers.

271

"""

272

raise NotImplementedError

273

274

def execute(self, query, params=None):

275

"""

276

Execute a database query.

277

278

Args:

279

query: SQL query string

280

params: Query parameters

281

282

This method must be implemented by handlers.

283

"""

284

raise NotImplementedError

285

286

def close(self):

287

"""

288

Close database connection.

289

290

This method must be implemented by handlers.

291

"""

292

raise NotImplementedError

293

```

294

295

### Implementing a Handler

296

297

```python

298

from cement import Handler

299

import sqlite3

300

301

class SQLiteHandler(Handler, DatabaseInterface):

302

"""

303

SQLite implementation of the database interface.

304

"""

305

306

class Meta:

307

label = 'sqlite'

308

interface = 'database'

309

config_defaults = {

310

'database_file': './app.db',

311

'timeout': 30

312

}

313

314

def __init__(self, **kw):

315

super().__init__(**kw)

316

self.connection = None

317

318

def connect(self, **kwargs):

319

"""Connect to SQLite database."""

320

db_file = kwargs.get('database_file', self._meta.config_defaults['database_file'])

321

timeout = kwargs.get('timeout', self._meta.config_defaults['timeout'])

322

323

self.connection = sqlite3.connect(db_file, timeout=timeout)

324

return self.connection

325

326

def execute(self, query, params=None):

327

"""Execute SQLite query."""

328

if not self.connection:

329

raise FrameworkError("Database not connected")

330

331

cursor = self.connection.cursor()

332

if params:

333

cursor.execute(query, params)

334

else:

335

cursor.execute(query)

336

337

return cursor.fetchall()

338

339

def close(self):

340

"""Close SQLite connection."""

341

if self.connection:

342

self.connection.close()

343

self.connection = None

344

```

345

346

### Using Interfaces and Handlers in an Application

347

348

```python

349

from cement import App, Controller, ex

350

351

class BaseController(Controller):

352

class Meta:

353

label = 'base'

354

355

@ex(help='test database operations')

356

def test_db(self):

357

"""Test database operations."""

358

# Get database handler through the app

359

db = self.app.handler.get('database', 'sqlite')()

360

361

try:

362

# Connect to database

363

db.connect(database_file='./test.db')

364

365

# Create table

366

db.execute('''

367

CREATE TABLE IF NOT EXISTS users (

368

id INTEGER PRIMARY KEY,

369

name TEXT,

370

email TEXT

371

)

372

''')

373

374

# Insert data

375

db.execute(

376

'INSERT INTO users (name, email) VALUES (?, ?)',

377

('John Doe', 'john@example.com')

378

)

379

380

# Query data

381

results = db.execute('SELECT * FROM users')

382

for row in results:

383

print(f'User: {row[1]}, Email: {row[2]}')

384

385

finally:

386

db.close()

387

388

class MyApp(App):

389

class Meta:

390

label = 'myapp'

391

base_controller = 'base'

392

handlers = [

393

BaseController,

394

SQLiteHandler

395

]

396

397

# Register the custom interface

398

def load(app):

399

app.interface.define(DatabaseInterface)

400

401

with MyApp() as app:

402

load(app)

403

app.run()

404

```

405

406

### Multiple Handler Implementations

407

408

```python

409

from cement import Handler

410

import psycopg2

411

412

class PostgreSQLHandler(Handler, DatabaseInterface):

413

"""

414

PostgreSQL implementation of the database interface.

415

"""

416

417

class Meta:

418

label = 'postgresql'

419

interface = 'database'

420

config_defaults = {

421

'host': 'localhost',

422

'port': 5432,

423

'database': 'myapp',

424

'user': 'postgres',

425

'password': ''

426

}

427

428

def __init__(self, **kw):

429

super().__init__(**kw)

430

self.connection = None

431

432

def connect(self, **kwargs):

433

"""Connect to PostgreSQL database."""

434

config = self._meta.config_defaults.copy()

435

config.update(kwargs)

436

437

self.connection = psycopg2.connect(

438

host=config['host'],

439

port=config['port'],

440

database=config['database'],

441

user=config['user'],

442

password=config['password']

443

)

444

return self.connection

445

446

def execute(self, query, params=None):

447

"""Execute PostgreSQL query."""

448

if not self.connection:

449

raise FrameworkError("Database not connected")

450

451

cursor = self.connection.cursor()

452

cursor.execute(query, params)

453

return cursor.fetchall()

454

455

def close(self):

456

"""Close PostgreSQL connection."""

457

if self.connection:

458

self.connection.close()

459

self.connection = None

460

461

class MyApp(App):

462

class Meta:

463

label = 'myapp'

464

base_controller = 'base'

465

handlers = [

466

BaseController,

467

SQLiteHandler,

468

PostgreSQLHandler

469

]

470

471

def load(app):

472

app.interface.define(DatabaseInterface)

473

474

with MyApp() as app:

475

load(app)

476

477

# Can choose which database handler to use

478

sqlite_db = app.handler.get('database', 'sqlite')()

479

postgres_db = app.handler.get('database', 'postgresql')()

480

481

app.run()

482

```

483

484

### Handler Override Options

485

486

```python

487

from cement import App, init_defaults

488

489

CONFIG = init_defaults('myapp')

490

CONFIG['myapp']['database_handler'] = 'sqlite' # Default handler

491

492

class MyApp(App):

493

class Meta:

494

label = 'myapp'

495

config_defaults = CONFIG

496

handler_override_options = {

497

'database': (['--database-handler'], {

498

'help': 'database handler to use',

499

'choices': ['sqlite', 'postgresql']

500

})

501

}

502

handlers = [

503

SQLiteHandler,

504

PostgreSQLHandler

505

]

506

507

def load(app):

508

app.interface.define(DatabaseInterface)

509

510

with MyApp() as app:

511

load(app)

512

513

# Handler can be overridden via command line:

514

# myapp --database-handler postgresql command

515

516

# Get the configured/overridden handler

517

handler_name = app.config.get('myapp', 'database_handler')

518

db = app.handler.get('database', handler_name)()

519

520

app.run()

521

```

522

523

### Checking Handler Registration

524

525

```python

526

from cement import App

527

528

class MyApp(App):

529

class Meta:

530

label = 'myapp'

531

handlers = [SQLiteHandler, PostgreSQLHandler]

532

533

def load(app):

534

app.interface.define(DatabaseInterface)

535

536

with MyApp() as app:

537

load(app)

538

app.setup()

539

540

# Check if interface is defined

541

if app.interface.defined('database'):

542

print("Database interface is defined")

543

544

# Check if handlers are registered

545

if app.handler.registered('database', 'sqlite'):

546

print("SQLite handler is registered")

547

548

if app.handler.registered('database', 'postgresql'):

549

print("PostgreSQL handler is registered")

550

551

# List all handlers for interface

552

handlers = app.handler.list('database')

553

print(f"Available database handlers: {handlers}")

554

```