or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

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

proxy-objects.mddocs/

0

# Proxy Objects

1

2

Transparent proxy objects that wrap other objects and delegate all operations to the wrapped object while allowing interception and modification of behavior. These proxies preserve the interface of the wrapped object while providing hooks for customization.

3

4

## Capabilities

5

6

### ObjectProxy

7

8

A transparent proxy object that wraps another object and delegates all operations to the wrapped object. The proxy preserves all attributes, methods, and special behaviors of the wrapped object.

9

10

```python { .api }

11

class ObjectProxy:

12

def __init__(self, wrapped):

13

"""

14

Create a transparent proxy for the wrapped object.

15

16

Args:

17

wrapped: The object to wrap

18

"""

19

20

@property

21

def __wrapped__(self):

22

"""Reference to the wrapped object."""

23

24

def __self_setattr__(self, name, value):

25

"""

26

Set attribute on the proxy itself rather than the wrapped object.

27

28

Args:

29

name (str): Attribute name

30

value: Attribute value

31

"""

32

33

# Properties that delegate to wrapped object

34

@property

35

def __module__(self): ...

36

37

@property

38

def __doc__(self): ...

39

40

@property

41

def __dict__(self): ...

42

43

@property

44

def __name__(self): ...

45

46

@property

47

def __class__(self): ...

48

```

49

50

**Usage Example:**

51

52

```python

53

import wrapt

54

55

class LoggingProxy(wrapt.ObjectProxy):

56

def __init__(self, wrapped):

57

super().__init__(wrapped)

58

self._self_call_count = 0

59

60

def __getattribute__(self, name):

61

if name.startswith('_self_'):

62

return object.__getattribute__(self, name)

63

64

self._self_call_count += 1

65

print(f"Accessing attribute: {name} (call #{self._self_call_count})")

66

return super().__getattribute__(name)

67

68

# Usage

69

my_list = [1, 2, 3]

70

proxy_list = LoggingProxy(my_list)

71

proxy_list.append(4) # Logs: "Accessing attribute: append (call #1)"

72

print(len(proxy_list)) # Logs: "Accessing attribute: __len__ (call #2)"

73

```

74

75

### CallableObjectProxy

76

77

Extends ObjectProxy to support callable objects by implementing the call protocol. Use this when wrapping functions, methods, or other callable objects.

78

79

```python { .api }

80

class CallableObjectProxy(ObjectProxy):

81

def __init__(self, wrapped):

82

"""

83

Create a transparent proxy for a callable object.

84

85

Args:

86

wrapped: The callable object to wrap (must be callable)

87

"""

88

89

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

90

"""

91

Make the proxy callable by delegating to wrapped object.

92

93

Args:

94

*args: Positional arguments

95

**kwargs: Keyword arguments

96

97

Returns:

98

Result of calling the wrapped object

99

"""

100

```

101

102

**Usage Example:**

103

104

```python

105

import wrapt

106

107

class TimingProxy(wrapt.CallableObjectProxy):

108

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

109

import time

110

start = time.time()

111

result = super().__call__(*args, **kwargs)

112

end = time.time()

113

print(f"Function took {end - start:.4f} seconds")

114

return result

115

116

def slow_function(n):

117

import time

118

time.sleep(n)

119

return f"Slept for {n} seconds"

120

121

# Wrap the function

122

timed_function = TimingProxy(slow_function)

123

result = timed_function(1) # Prints timing information

124

```

125

126

### PartialCallableObjectProxy

127

128

Similar to `functools.partial` but implemented as a proxy object. Pre-fills some arguments and keyword arguments for a callable, creating a new callable with fewer parameters.

129

130

```python { .api }

131

class PartialCallableObjectProxy(ObjectProxy):

132

def __init__(self, wrapped, *args, **kwargs):

133

"""

134

Create a partial application of a callable.

135

136

Args:

137

wrapped: The callable to wrap (must be callable)

138

*args: Positional arguments to pre-fill

139

**kwargs: Keyword arguments to pre-fill

140

"""

141

142

@property

143

def _self_args(self):

144

"""Tuple of stored positional arguments."""

145

146

@property

147

def _self_kwargs(self):

148

"""Dictionary of stored keyword arguments."""

149

150

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

151

"""

152

Call the wrapped function with pre-filled and new arguments.

153

154

Args:

155

*args: Additional positional arguments

156

**kwargs: Additional keyword arguments

157

158

Returns:

159

Result of calling wrapped function with combined arguments

160

"""

161

```

162

163

**Usage Example:**

164

165

```python

166

import wrapt

167

168

def greet(greeting, name, punctuation="!"):

169

return f"{greeting}, {name}{punctuation}"

170

171

# Create partial applications

172

say_hello = wrapt.PartialCallableObjectProxy(greet, "Hello")

173

say_goodbye = wrapt.PartialCallableObjectProxy(greet, "Goodbye", punctuation=".")

174

175

# Use the partial functions

176

print(say_hello("Alice")) # "Hello, Alice!"

177

print(say_goodbye("Bob")) # "Goodbye, Bob."

178

print(say_hello("Charlie", "?")) # "Hello, Charlie?"

179

```

180

181

## Advanced Usage

182

183

### Custom Proxy Classes

184

185

You can subclass ObjectProxy to create specialized proxy behaviors:

186

187

```python

188

import wrapt

189

190

class ValidationProxy(wrapt.ObjectProxy):

191

def __init__(self, wrapped, validator=None):

192

super().__init__(wrapped)

193

self._self_validator = validator or (lambda x: True)

194

195

def __setattr__(self, name, value):

196

if not name.startswith('_self_') and not self._self_validator(value):

197

raise ValueError(f"Invalid value for {name}: {value}")

198

super().__setattr__(name, value)

199

200

# Usage with validation

201

class Person:

202

def __init__(self, name, age):

203

self.name = name

204

self.age = age

205

206

def age_validator(value):

207

return isinstance(value, int) and 0 <= value <= 150

208

209

person = Person("Alice", 30)

210

validated_person = ValidationProxy(person, age_validator)

211

212

validated_person.age = 25 # OK

213

# validated_person.age = -5 # Raises ValueError

214

```

215

216

### Proxy Introspection

217

218

All proxy objects maintain access to the wrapped object and preserve introspection capabilities:

219

220

```python

221

import wrapt

222

223

def my_function():

224

"""A test function."""

225

return "Hello"

226

227

proxy = wrapt.CallableObjectProxy(my_function)

228

229

# Access wrapped object

230

assert proxy.__wrapped__ is my_function

231

232

# Introspection works

233

assert proxy.__name__ == "my_function"

234

assert proxy.__doc__ == "A test function."

235

assert callable(proxy)

236

237

# isinstance and hasattr work correctly

238

assert isinstance(proxy, type(my_function))

239

assert hasattr(proxy, '__call__')

240

```

241

242

## Error Handling

243

244

Proxy objects raise `NotImplementedError` for operations that cannot be safely proxied:

245

246

- `__copy__()` and `__deepcopy__()`: Copying behavior is complex and object-specific

247

- `__reduce__()` and `__reduce_ex__()`: Pickling support requires custom implementation

248

249

For these operations, either implement them in your proxy subclass or handle them explicitly in your code.