or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

client-operations.mdexception-handling.mdindex.mdplugin-system.mdtransport-settings.mdwsse-security.mdxsd-types.md

plugin-system.mddocs/

0

# Plugin System

1

2

Extensible plugin architecture for intercepting and modifying SOAP requests and responses. Plugins enable custom logging, authentication, caching, debugging, and other cross-cutting concerns without modifying core zeep behavior.

3

4

## Capabilities

5

6

### Base Plugin Class

7

8

Foundation for all zeep plugins with request/response interception points.

9

10

```python { .api }

11

class Plugin:

12

def ingress(self, envelope, http_headers: dict, operation):

13

"""

14

Process incoming SOAP responses.

15

16

Called when receiving responses from SOAP services.

17

18

Parameters:

19

- envelope: Response envelope (lxml.etree._Element)

20

- http_headers: HTTP response headers dict

21

- operation: Operation instance that generated the request

22

23

Returns:

24

tuple: (modified_envelope, modified_headers) or None to keep unchanged

25

"""

26

27

def egress(self, envelope, http_headers: dict, operation, binding_options: dict):

28

"""

29

Process outgoing SOAP requests.

30

31

Called before sending requests to SOAP services.

32

33

Parameters:

34

- envelope: Request envelope (lxml.etree._Element)

35

- http_headers: HTTP request headers dict

36

- operation: Operation instance being called

37

- binding_options: Binding-specific options dict

38

39

Returns:

40

tuple: (modified_envelope, modified_headers) or None to keep unchanged

41

"""

42

```

43

44

### History Plugin

45

46

Built-in plugin for tracking request/response history for debugging and auditing.

47

48

```python { .api }

49

class HistoryPlugin(Plugin):

50

def __init__(self, maxlen: int = 1):

51

"""

52

Create history tracking plugin.

53

54

Parameters:

55

- maxlen: Maximum number of request/response pairs to store

56

"""

57

58

@property

59

def last_sent(self):

60

"""Last sent request envelope."""

61

62

@property

63

def last_received(self):

64

"""Last received response envelope."""

65

```

66

67

### Plugin Application Functions

68

69

Utility functions for applying plugins to requests and responses.

70

71

```python { .api }

72

def apply_egress(client, envelope, http_headers: dict, operation, binding_options: dict):

73

"""

74

Apply all egress plugins to outgoing request.

75

76

Parameters:

77

- client: Client instance with plugins

78

- envelope: Request envelope

79

- http_headers: HTTP headers

80

- operation: Operation being called

81

- binding_options: Binding options

82

83

Returns:

84

tuple: (processed_envelope, processed_headers)

85

"""

86

87

def apply_ingress(client, envelope, http_headers: dict, operation):

88

"""

89

Apply all ingress plugins to incoming response.

90

91

Parameters:

92

- client: Client instance with plugins

93

- envelope: Response envelope

94

- http_headers: HTTP headers

95

- operation: Operation that generated request

96

97

Returns:

98

tuple: (processed_envelope, processed_headers)

99

"""

100

```

101

102

## Usage Examples

103

104

### Basic Plugin Usage

105

106

```python

107

from zeep import Client

108

from zeep.plugins import HistoryPlugin

109

110

# Add history plugin to track requests/responses

111

history = HistoryPlugin()

112

client = Client('http://example.com/service.wsdl', plugins=[history])

113

114

# Make SOAP call

115

result = client.service.SomeOperation(param='value')

116

117

# Inspect request/response history

118

print("Last sent request:")

119

print(history.last_sent)

120

121

print("Last received response:")

122

print(history.last_received)

123

```

124

125

### Custom Logging Plugin

126

127

```python

128

import logging

129

from zeep import Client

130

from zeep.plugins import Plugin

131

132

class LoggingPlugin(Plugin):

133

def __init__(self, logger_name='zeep.plugin'):

134

self.logger = logging.getLogger(logger_name)

135

136

def egress(self, envelope, http_headers, operation, binding_options):

137

self.logger.info(f"Sending request to operation: {operation.name}")

138

self.logger.debug(f"Request envelope: {envelope}")

139

return envelope, http_headers

140

141

def ingress(self, envelope, http_headers, operation):

142

self.logger.info(f"Received response from operation: {operation.name}")

143

self.logger.debug(f"Response envelope: {envelope}")

144

return envelope, http_headers

145

146

# Set up logging

147

logging.basicConfig(level=logging.INFO)

148

149

# Use custom plugin

150

logging_plugin = LoggingPlugin()

151

client = Client('http://example.com/service.wsdl', plugins=[logging_plugin])

152

153

result = client.service.SomeOperation(param='value')

154

```

155

156

### Authentication Plugin

157

158

```python

159

from zeep import Client

160

from zeep.plugins import Plugin

161

import base64

162

163

class CustomAuthPlugin(Plugin):

164

def __init__(self, api_key):

165

self.api_key = api_key

166

167

def egress(self, envelope, http_headers, operation, binding_options):

168

# Add custom authentication header

169

http_headers['X-API-Key'] = self.api_key

170

http_headers['Authorization'] = f'Bearer {self.api_key}'

171

return envelope, http_headers

172

173

# Use authentication plugin

174

auth_plugin = CustomAuthPlugin('your-api-key-here')

175

client = Client('http://example.com/service.wsdl', plugins=[auth_plugin])

176

177

result = client.service.AuthenticatedOperation(param='value')

178

```

179

180

### Request/Response Modification Plugin

181

182

```python

183

from zeep import Client

184

from zeep.plugins import Plugin

185

from lxml import etree

186

187

class RequestModifierPlugin(Plugin):

188

def egress(self, envelope, http_headers, operation, binding_options):

189

# Add custom SOAP header

190

header = envelope.find('.//{http://schemas.xmlsoap.org/soap/envelope/}Header')

191

if header is None:

192

header = etree.Element('{http://schemas.xmlsoap.org/soap/envelope/}Header')

193

envelope.insert(0, header)

194

195

# Add custom header element

196

custom_header = etree.SubElement(header, '{http://example.com}CustomHeader')

197

custom_header.text = 'Custom Value'

198

199

return envelope, http_headers

200

201

def ingress(self, envelope, http_headers, operation):

202

# Log response status from custom header

203

status_elem = envelope.find('.//{http://example.com}Status')

204

if status_elem is not None:

205

print(f"Operation status: {status_elem.text}")

206

207

return envelope, http_headers

208

209

modifier_plugin = RequestModifierPlugin()

210

client = Client('http://example.com/service.wsdl', plugins=[modifier_plugin])

211

```

212

213

### Multiple Plugins

214

215

```python

216

from zeep import Client

217

from zeep.plugins import Plugin, HistoryPlugin

218

219

class MetricsPlugin(Plugin):

220

def __init__(self):

221

self.request_count = 0

222

self.response_count = 0

223

224

def egress(self, envelope, http_headers, operation, binding_options):

225

self.request_count += 1

226

print(f"Request #{self.request_count} to {operation.name}")

227

return envelope, http_headers

228

229

def ingress(self, envelope, http_headers, operation):

230

self.response_count += 1

231

print(f"Response #{self.response_count} from {operation.name}")

232

return envelope, http_headers

233

234

# Use multiple plugins together

235

history = HistoryPlugin(maxlen=5)

236

metrics = MetricsPlugin()

237

auth = CustomAuthPlugin('api-key')

238

239

client = Client(

240

'http://example.com/service.wsdl',

241

plugins=[auth, metrics, history]

242

)

243

244

# All plugins will be applied in order

245

result = client.service.SomeOperation(param='value')

246

247

print(f"Total requests: {metrics.request_count}")

248

print(f"Total responses: {metrics.response_count}")

249

```

250

251

### Plugin for Error Handling

252

253

```python

254

from zeep import Client

255

from zeep.plugins import Plugin

256

from zeep.exceptions import Fault

257

258

class ErrorHandlingPlugin(Plugin):

259

def __init__(self):

260

self.error_count = 0

261

262

def ingress(self, envelope, http_headers, operation):

263

# Check for SOAP faults and custom error handling

264

fault = envelope.find('.//{http://schemas.xmlsoap.org/soap/envelope/}Fault')

265

if fault is not None:

266

self.error_count += 1

267

print(f"SOAP fault detected in {operation.name}")

268

269

# Could log to external system, send alerts, etc.

270

271

return envelope, http_headers

272

273

error_plugin = ErrorHandlingPlugin()

274

client = Client('http://example.com/service.wsdl', plugins=[error_plugin])

275

276

try:

277

result = client.service.OperationThatMightFail(param='value')

278

except Fault as e:

279

print(f"Total errors encountered: {error_plugin.error_count}")

280

raise

281

```

282

283

### Plugin Context and State

284

285

```python

286

from zeep import Client

287

from zeep.plugins import Plugin

288

import time

289

290

class TimingPlugin(Plugin):

291

def __init__(self):

292

self.operation_times = {}

293

self._start_times = {}

294

295

def egress(self, envelope, http_headers, operation, binding_options):

296

# Record start time for this operation

297

self._start_times[id(envelope)] = time.time()

298

return envelope, http_headers

299

300

def ingress(self, envelope, http_headers, operation):

301

# Calculate operation duration

302

envelope_id = id(envelope)

303

if envelope_id in self._start_times:

304

duration = time.time() - self._start_times.pop(envelope_id)

305

306

op_name = operation.name

307

if op_name not in self.operation_times:

308

self.operation_times[op_name] = []

309

310

self.operation_times[op_name].append(duration)

311

print(f"{op_name} took {duration:.2f} seconds")

312

313

return envelope, http_headers

314

315

def get_average_time(self, operation_name):

316

times = self.operation_times.get(operation_name, [])

317

return sum(times) / len(times) if times else 0

318

319

timing_plugin = TimingPlugin()

320

client = Client('http://example.com/service.wsdl', plugins=[timing_plugin])

321

322

# Make several calls

323

for i in range(3):

324

result = client.service.SomeOperation(param=f'value{i}')

325

326

# Check performance metrics

327

avg_time = timing_plugin.get_average_time('SomeOperation')

328

print(f"Average operation time: {avg_time:.2f} seconds")

329

```