or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

decorators.mdfunction-wrappers.mdindex.mdpatching.mdproxy-objects.mdutilities.md

decorators.mddocs/

0

# Decorator Creation

1

2

Universal decorator factory and synchronization utilities for creating robust decorators with proper signature preservation. These tools enable the creation of decorators that work correctly across functions, methods, classes, and other callable objects while preserving introspection capabilities.

3

4

## Capabilities

5

6

### Universal Decorator Factory

7

8

#### decorator

9

10

The main decorator factory for creating robust decorators with proper signature preservation. This is the recommended way to create decorators in wrapt.

11

12

```python { .api }

13

def decorator(wrapper=None, enabled=None, adapter=None, proxy=FunctionWrapper):

14

"""

15

Universal decorator factory for creating robust decorators.

16

17

Args:

18

wrapper: Wrapper function with signature (wrapped, instance, args, kwargs) -> result

19

enabled: Boolean or callable to enable/disable decorator (optional)

20

adapter: Function or signature spec for signature adaptation (optional)

21

proxy: Proxy class to use, defaults to FunctionWrapper (optional)

22

23

Returns:

24

Decorator function or partial decorator if wrapper not provided

25

"""

26

```

27

28

**Basic Usage:**

29

30

```python

31

import wrapt

32

33

@wrapt.decorator

34

def my_decorator(wrapped, instance, args, kwargs):

35

print(f"Calling {wrapped.__name__}")

36

result = wrapped(*args, **kwargs)

37

print(f"Result: {result}")

38

return result

39

40

@my_decorator

41

def greet(name):

42

return f"Hello, {name}!"

43

44

result = greet("Alice") # Logs call and result

45

```

46

47

**Partial Application:**

48

49

```python

50

import wrapt

51

52

# Create reusable decorator factory

53

def timing_decorator(enabled=True):

54

@wrapt.decorator(enabled=enabled)

55

def wrapper(wrapped, instance, args, kwargs):

56

import time

57

start = time.time()

58

result = wrapped(*args, **kwargs)

59

end = time.time()

60

print(f"{wrapped.__name__} took {end - start:.4f}s")

61

return result

62

return wrapper

63

64

# Use with different configurations

65

@timing_decorator(enabled=True)

66

def slow_function():

67

import time

68

time.sleep(0.1)

69

return "done"

70

71

@timing_decorator(enabled=False)

72

def fast_function():

73

return "instant"

74

75

slow_function() # Shows timing

76

fast_function() # No timing output

77

```

78

79

### Adapter Factories

80

81

#### AdapterFactory

82

83

Base class for creating adapter factories that modify function signatures or behavior.

84

85

```python { .api }

86

class AdapterFactory:

87

"""

88

Base class for adapter factories.

89

"""

90

91

def __call__(self, wrapped):

92

"""

93

Abstract method to be implemented by subclasses.

94

95

Args:

96

wrapped: The function being adapted

97

98

Returns:

99

Adapter function or signature specification

100

"""

101

```

102

103

#### adapter_factory

104

105

Alias for `DelegatedAdapterFactory` class - a factory that delegates to another factory function.

106

107

```python { .api }

108

adapter_factory = DelegatedAdapterFactory

109

# Usage: adapter_factory(factory_function)

110

```

111

112

**Usage Example:**

113

114

```python

115

import wrapt

116

import inspect

117

118

def signature_adapter_factory(wrapped):

119

"""Factory that creates signature adapter."""

120

sig = inspect.signature(wrapped)

121

122

def adapter(*args, **kwargs):

123

# Validate arguments against signature

124

bound = sig.bind(*args, **kwargs)

125

bound.apply_defaults()

126

return wrapped(**bound.arguments)

127

128

return adapter

129

130

@wrapt.decorator(adapter=wrapt.adapter_factory(signature_adapter_factory))

131

def validating_decorator(wrapped, instance, args, kwargs):

132

print(f"Validated call to {wrapped.__name__}")

133

return wrapped(*args, **kwargs)

134

135

@validating_decorator

136

def add_numbers(a: int, b: int = 10) -> int:

137

return a + b

138

139

result = add_numbers(5) # Uses default b=10, validated

140

```

141

142

### Synchronization

143

144

#### synchronized

145

146

Decorator and context manager for thread synchronization using locks. Provides automatic thread safety for functions and methods.

147

148

```python { .api }

149

def synchronized(wrapped):

150

"""

151

Decorator/context manager for thread synchronization.

152

153

Args:

154

wrapped: Function to synchronize OR lock object with acquire()/release()

155

156

Returns:

157

Synchronized decorator or context manager

158

"""

159

```

160

161

**As Decorator:**

162

163

```python

164

import wrapt

165

import threading

166

import time

167

168

class Counter:

169

def __init__(self):

170

self.value = 0

171

172

@wrapt.synchronized

173

def increment(self):

174

# This method is thread-safe

175

temp = self.value

176

time.sleep(0.001) # Simulate work

177

self.value = temp + 1

178

179

@wrapt.synchronized

180

def get_value(self):

181

return self.value

182

183

counter = Counter()

184

185

def worker():

186

for _ in range(100):

187

counter.increment()

188

189

threads = [threading.Thread(target=worker) for _ in range(10)]

190

for t in threads:

191

t.start()

192

for t in threads:

193

t.join()

194

195

print(counter.get_value()) # Should be 1000, not less due to race conditions

196

```

197

198

**As Context Manager:**

199

200

```python

201

import wrapt

202

import threading

203

204

# Using custom lock

205

my_lock = threading.RLock()

206

207

@wrapt.synchronized(my_lock)

208

def critical_section():

209

print("In critical section")

210

time.sleep(0.1)

211

212

# Or as context manager

213

with wrapt.synchronized(my_lock):

214

print("Also in critical section")

215

time.sleep(0.1)

216

```

217

218

## Advanced Usage

219

220

### Conditional Decorators

221

222

Create decorators that are conditionally applied:

223

224

```python

225

import wrapt

226

import os

227

228

def debug_decorator(enabled=None):

229

if enabled is None:

230

enabled = lambda: os.environ.get('DEBUG') == '1'

231

232

@wrapt.decorator(enabled=enabled)

233

def wrapper(wrapped, instance, args, kwargs):

234

print(f"DEBUG: {wrapped.__name__} called with {args}, {kwargs}")

235

result = wrapped(*args, **kwargs)

236

print(f"DEBUG: {wrapped.__name__} returned {result}")

237

return result

238

return wrapper

239

240

@debug_decorator()

241

def my_function(x, y):

242

return x + y

243

244

# Only logs when DEBUG=1 environment variable is set

245

result = my_function(2, 3)

246

```

247

248

### Class Decorators

249

250

The decorator function works with classes as well as functions:

251

252

```python

253

import wrapt

254

255

@wrapt.decorator

256

def class_wrapper(wrapped, instance, args, kwargs):

257

print(f"Creating instance of {wrapped.__name__}")

258

instance = wrapped(*args, **kwargs)

259

print(f"Created: {instance}")

260

return instance

261

262

@class_wrapper

263

class MyClass:

264

def __init__(self, name):

265

self.name = name

266

267

def __repr__(self):

268

return f"MyClass({self.name})"

269

270

obj = MyClass("test") # Logs creation

271

```

272

273

### Method-Aware Decorators

274

275

Decorators that behave differently for functions vs methods:

276

277

```python

278

import wrapt

279

280

@wrapt.decorator

281

def smart_decorator(wrapped, instance, args, kwargs):

282

if instance is None:

283

print(f"Function call: {wrapped.__name__}")

284

else:

285

print(f"Method call: {wrapped.__name__} on {type(instance).__name__}")

286

287

return wrapped(*args, **kwargs)

288

289

@smart_decorator

290

def standalone_function():

291

return "function result"

292

293

class MyClass:

294

@smart_decorator

295

def method(self):

296

return "method result"

297

298

standalone_function() # Logs: "Function call: standalone_function"

299

300

obj = MyClass()

301

obj.method() # Logs: "Method call: method on MyClass"

302

```

303

304

### Decorator Chains

305

306

Multiple decorators work correctly with wrapt:

307

308

```python

309

import wrapt

310

311

@wrapt.decorator

312

def decorator1(wrapped, instance, args, kwargs):

313

print("Decorator 1 - before")

314

result = wrapped(*args, **kwargs)

315

print("Decorator 1 - after")

316

return result

317

318

@wrapt.decorator

319

def decorator2(wrapped, instance, args, kwargs):

320

print("Decorator 2 - before")

321

result = wrapped(*args, **kwargs)

322

print("Decorator 2 - after")

323

return result

324

325

@decorator1

326

@decorator2

327

def my_function():

328

print("Function executing")

329

return "result"

330

331

my_function()

332

# Output:

333

# Decorator 1 - before

334

# Decorator 2 - before

335

# Function executing

336

# Decorator 2 - after

337

# Decorator 1 - after

338

```

339

340

### Custom Proxy Classes

341

342

Use custom proxy classes with the decorator:

343

344

```python

345

import wrapt

346

347

class CustomProxy(wrapt.FunctionWrapper):

348

def __init__(self, wrapped, wrapper):

349

super().__init__(wrapped, wrapper)

350

self._self_call_count = 0

351

352

def __call__(self, *args, **kwargs):

353

self._self_call_count += 1

354

print(f"Call #{self._self_call_count}")

355

return super().__call__(*args, **kwargs)

356

357

@wrapt.decorator(proxy=CustomProxy)

358

def counting_decorator(wrapped, instance, args, kwargs):

359

return wrapped(*args, **kwargs)

360

361

@counting_decorator

362

def my_function():

363

return "result"

364

365

my_function() # Call #1

366

my_function() # Call #2

367

```

368

369

## Signature Preservation

370

371

The decorator function preserves function signatures for introspection:

372

373

```python

374

import wrapt

375

import inspect

376

377

@wrapt.decorator

378

def preserving_decorator(wrapped, instance, args, kwargs):

379

return wrapped(*args, **kwargs)

380

381

@preserving_decorator

382

def example_function(a: int, b: str = "default") -> str:

383

"""Example function with type hints."""

384

return f"{a}: {b}"

385

386

# Signature is preserved

387

sig = inspect.signature(example_function)

388

print(sig) # (a: int, b: str = 'default') -> str

389

390

# Docstring is preserved

391

print(example_function.__doc__) # "Example function with type hints."

392

393

# Name is preserved

394

print(example_function.__name__) # "example_function"

395

```

396

397

## Error Handling

398

399

Decorators created with wrapt properly preserve exception information:

400

401

```python

402

import wrapt

403

404

@wrapt.decorator

405

def error_handling_decorator(wrapped, instance, args, kwargs):

406

try:

407

return wrapped(*args, **kwargs)

408

except Exception as e:

409

print(f"Exception in {wrapped.__name__}: {e}")

410

raise # Re-raise preserving traceback

411

412

@error_handling_decorator

413

def failing_function():

414

raise ValueError("Something went wrong")

415

416

try:

417

failing_function()

418

except ValueError:

419

import traceback

420

traceback.print_exc() # Shows original traceback

421

```