Skip to content

Commit 030cfc9

Browse files
Update wrapt (#993)
* Update wrapt to 1.16.0 * Import duplicate functions directly from wrapt * Update object wrappers for wrapt 1.16.0 * Add warning to wrapt duplicate code * Linting * Use super rather than hard coded Object proxy * Formatting * Add test file for wrapper attributes * Linting * Add descriptions to assertions * Overhaul test suite for clarity * Move functions into fixtures * [Mega-Linter] Apply linters fixes * Bump tests * Fix typo * Larger timeout for protobuf --------- Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> Co-authored-by: TimPansino <[email protected]>
1 parent cd74bc4 commit 030cfc9

File tree

11 files changed

+552
-490
lines changed

11 files changed

+552
-490
lines changed

newrelic/common/object_wrapper.py

+55-157
Original file line numberDiff line numberDiff line change
@@ -19,16 +19,19 @@
1919
2020
"""
2121

22-
import sys
2322
import inspect
2423

25-
from newrelic.packages import six
26-
27-
from newrelic.packages.wrapt import (ObjectProxy as _ObjectProxy,
28-
FunctionWrapper as _FunctionWrapper,
29-
BoundFunctionWrapper as _BoundFunctionWrapper)
30-
31-
from newrelic.packages.wrapt.wrappers import _FunctionWrapperBase
24+
from newrelic.packages.wrapt import BoundFunctionWrapper as _BoundFunctionWrapper
25+
from newrelic.packages.wrapt import CallableObjectProxy as _CallableObjectProxy
26+
from newrelic.packages.wrapt import FunctionWrapper as _FunctionWrapper
27+
from newrelic.packages.wrapt import ObjectProxy as _ObjectProxy
28+
from newrelic.packages.wrapt import ( # noqa: F401; pylint: disable=W0611
29+
apply_patch,
30+
resolve_path,
31+
wrap_object,
32+
wrap_object_attribute,
33+
)
34+
from newrelic.packages.wrapt.__wrapt__ import _FunctionWrapperBase
3235

3336
# We previously had our own pure Python implementation of the generic
3437
# object wrapper but we now defer to using the wrapt module as its C
@@ -47,28 +50,36 @@
4750
# ObjectProxy or FunctionWrapper should be used going forward.
4851

4952

50-
class _ObjectWrapperBase(object):
53+
class ObjectProxy(_ObjectProxy):
54+
"""
55+
This class provides method overrides for all object wrappers used by the
56+
agent. These methods allow attributes to be defined with the special prefix
57+
_nr_ to be interpretted as attributes on the wrapper, rather than the
58+
wrapped object. Inheriting from the base class wrapt.ObjectProxy preserves
59+
method resolution order (MRO) through multiple inheritance.
60+
(See https://www.python.org/download/releases/2.3/mro/).
61+
"""
5162

5263
def __setattr__(self, name, value):
53-
if name.startswith('_nr_'):
54-
name = name.replace('_nr_', '_self_', 1)
64+
if name.startswith("_nr_"):
65+
name = name.replace("_nr_", "_self_", 1)
5566
setattr(self, name, value)
5667
else:
57-
_ObjectProxy.__setattr__(self, name, value)
68+
super(ObjectProxy, self).__setattr__(name, value)
5869

5970
def __getattr__(self, name):
60-
if name.startswith('_nr_'):
61-
name = name.replace('_nr_', '_self_', 1)
71+
if name.startswith("_nr_"):
72+
name = name.replace("_nr_", "_self_", 1)
6273
return getattr(self, name)
6374
else:
64-
return _ObjectProxy.__getattr__(self, name)
75+
return super(ObjectProxy, self).__getattr__(name)
6576

6677
def __delattr__(self, name):
67-
if name.startswith('_nr_'):
68-
name = name.replace('_nr_', '_self_', 1)
78+
if name.startswith("_nr_"):
79+
name = name.replace("_nr_", "_self_", 1)
6980
delattr(self, name)
7081
else:
71-
_ObjectProxy.__delattr__(self, name)
82+
super(ObjectProxy, self).__delattr__(name)
7283

7384
@property
7485
def _nr_next_object(self):
@@ -79,8 +90,7 @@ def _nr_last_object(self):
7990
try:
8091
return self._self_last_object
8192
except AttributeError:
82-
self._self_last_object = getattr(self.__wrapped__,
83-
'_nr_last_object', self.__wrapped__)
93+
self._self_last_object = getattr(self.__wrapped__, "_nr_last_object", self.__wrapped__)
8494
return self._self_last_object
8595

8696
@property
@@ -96,166 +106,45 @@ def _nr_parent(self):
96106
return self._self_parent
97107

98108

99-
class _NRBoundFunctionWrapper(_ObjectWrapperBase, _BoundFunctionWrapper):
109+
class _NRBoundFunctionWrapper(ObjectProxy, _BoundFunctionWrapper):
100110
pass
101111

102112

103-
class FunctionWrapper(_ObjectWrapperBase, _FunctionWrapper):
113+
class FunctionWrapper(ObjectProxy, _FunctionWrapper):
104114
__bound_function_wrapper__ = _NRBoundFunctionWrapper
105115

106116

107-
class ObjectProxy(_ObjectProxy):
108-
109-
def __setattr__(self, name, value):
110-
if name.startswith('_nr_'):
111-
name = name.replace('_nr_', '_self_', 1)
112-
setattr(self, name, value)
113-
else:
114-
_ObjectProxy.__setattr__(self, name, value)
115-
116-
def __getattr__(self, name):
117-
if name.startswith('_nr_'):
118-
name = name.replace('_nr_', '_self_', 1)
119-
return getattr(self, name)
120-
else:
121-
return _ObjectProxy.__getattr__(self, name)
122-
123-
def __delattr__(self, name):
124-
if name.startswith('_nr_'):
125-
name = name.replace('_nr_', '_self_', 1)
126-
delattr(self, name)
127-
else:
128-
_ObjectProxy.__delattr__(self, name)
129-
130-
@property
131-
def _nr_next_object(self):
132-
return self.__wrapped__
133-
134-
@property
135-
def _nr_last_object(self):
136-
try:
137-
return self._self_last_object
138-
except AttributeError:
139-
self._self_last_object = getattr(self.__wrapped__,
140-
'_nr_last_object', self.__wrapped__)
141-
return self._self_last_object
142-
143-
144-
class CallableObjectProxy(ObjectProxy):
117+
class CallableObjectProxy(ObjectProxy, _CallableObjectProxy):
118+
pass
145119

146-
def __call__(self, *args, **kwargs):
147-
return self.__wrapped__(*args, **kwargs)
148120

149121
# The ObjectWrapper class needs to be deprecated and removed once all our
150122
# own code no longer uses it. It reaches down into what are wrapt internals
151123
# at present which shouldn't be doing.
152124

153125

154-
class ObjectWrapper(_ObjectWrapperBase, _FunctionWrapperBase):
126+
class ObjectWrapper(ObjectProxy, _FunctionWrapperBase):
155127
__bound_function_wrapper__ = _NRBoundFunctionWrapper
156128

157129
def __init__(self, wrapped, instance, wrapper):
158130
if isinstance(wrapped, classmethod):
159-
binding = 'classmethod'
131+
binding = "classmethod"
160132
elif isinstance(wrapped, staticmethod):
161-
binding = 'staticmethod'
133+
binding = "staticmethod"
162134
else:
163-
binding = 'function'
164-
165-
super(ObjectWrapper, self).__init__(wrapped, instance, wrapper,
166-
binding=binding)
167-
168-
169-
# Helper functions for performing monkey patching.
135+
binding = "function"
170136

137+
super(ObjectWrapper, self).__init__(wrapped, instance, wrapper, binding=binding)
171138

172-
def resolve_path(module, name):
173-
if isinstance(module, six.string_types):
174-
__import__(module)
175-
module = sys.modules[module]
176-
177-
parent = module
178-
179-
path = name.split('.')
180-
attribute = path[0]
181-
182-
original = getattr(parent, attribute)
183-
for attribute in path[1:]:
184-
parent = original
185-
186-
# We can't just always use getattr() because in doing
187-
# that on a class it will cause binding to occur which
188-
# will complicate things later and cause some things not
189-
# to work. For the case of a class we therefore access
190-
# the __dict__ directly. To cope though with the wrong
191-
# class being given to us, or a method being moved into
192-
# a base class, we need to walk the class hierarchy to
193-
# work out exactly which __dict__ the method was defined
194-
# in, as accessing it from __dict__ will fail if it was
195-
# not actually on the class given. Fallback to using
196-
# getattr() if we can't find it. If it truly doesn't
197-
# exist, then that will fail.
198-
199-
if inspect.isclass(original):
200-
for cls in inspect.getmro(original):
201-
if attribute in vars(cls):
202-
original = vars(cls)[attribute]
203-
break
204-
else:
205-
original = getattr(original, attribute)
206-
207-
else:
208-
original = getattr(original, attribute)
209-
210-
return (parent, attribute, original)
211-
212-
213-
def apply_patch(parent, attribute, replacement):
214-
setattr(parent, attribute, replacement)
215-
216-
217-
def wrap_object(module, name, factory, args=(), kwargs={}):
218-
(parent, attribute, original) = resolve_path(module, name)
219-
wrapper = factory(original, *args, **kwargs)
220-
apply_patch(parent, attribute, wrapper)
221-
return wrapper
222-
223-
# Function for apply a proxy object to an attribute of a class instance.
224-
# The wrapper works by defining an attribute of the same name on the
225-
# class which is a descriptor and which intercepts access to the
226-
# instance attribute. Note that this cannot be used on attributes which
227-
# are themselves defined by a property object.
228-
229-
230-
class AttributeWrapper(object):
231-
232-
def __init__(self, attribute, factory, args, kwargs):
233-
self.attribute = attribute
234-
self.factory = factory
235-
self.args = args
236-
self.kwargs = kwargs
237-
238-
def __get__(self, instance, owner):
239-
value = instance.__dict__[self.attribute]
240-
return self.factory(value, *self.args, **self.kwargs)
241-
242-
def __set__(self, instance, value):
243-
instance.__dict__[self.attribute] = value
244-
245-
def __delete__(self, instance):
246-
del instance.__dict__[self.attribute]
247-
248-
249-
def wrap_object_attribute(module, name, factory, args=(), kwargs={}):
250-
path, attribute = name.rsplit('.', 1)
251-
parent = resolve_path(module, path)[2]
252-
wrapper = AttributeWrapper(attribute, factory, args, kwargs)
253-
apply_patch(parent, attribute, wrapper)
254-
return wrapper
255139

256140
# Function for creating a decorator for applying to functions, as well as
257141
# short cut functions for applying wrapper functions via monkey patching.
258142

143+
# WARNING: These functions are reproduced directly from wrapt, but using
144+
# our FunctionWrapper class which includes the _nr_ attriubte overrides
145+
# that are inherited from our subclass of wrapt.ObjectProxy.These MUST be
146+
# kept in sync with wrapt when upgrading, or drift may introduce bugs.
147+
259148

260149
def function_wrapper(wrapper):
261150
def _wrapper(wrapped, instance, args, kwargs):
@@ -267,16 +156,18 @@ def _wrapper(wrapped, instance, args, kwargs):
267156
else:
268157
target_wrapper = wrapper.__get__(instance, type(instance))
269158
return FunctionWrapper(target_wrapped, target_wrapper)
159+
270160
return FunctionWrapper(wrapper, _wrapper)
271161

272162

273163
def wrap_function_wrapper(module, name, wrapper):
274164
return wrap_object(module, name, FunctionWrapper, (wrapper,))
275165

276166

277-
def patch_function_wrapper(module, name):
167+
def patch_function_wrapper(module, name, enabled=None):
278168
def _wrapper(wrapper):
279-
return wrap_object(module, name, FunctionWrapper, (wrapper,))
169+
return wrap_object(module, name, FunctionWrapper, (wrapper, enabled))
170+
280171
return _wrapper
281172

282173

@@ -299,10 +190,14 @@ def _execute(wrapped, instance, args, kwargs):
299190
return wrapped(*args, **kwargs)
300191
finally:
301192
setattr(parent, attribute, original)
193+
302194
return FunctionWrapper(target_wrapped, _execute)
195+
303196
return FunctionWrapper(wrapper, _wrapper)
197+
304198
return _decorator
305199

200+
306201
# Generic decorators for performing actions before and after a wrapped
307202
# function is called, or modifying the inbound arguments or return value.
308203

@@ -315,6 +210,7 @@ def _wrapper(wrapped, instance, args, kwargs):
315210
else:
316211
function(*args, **kwargs)
317212
return wrapped(*args, **kwargs)
213+
318214
return _wrapper
319215

320216

@@ -335,6 +231,7 @@ def _wrapper(wrapped, instance, args, kwargs):
335231
else:
336232
function(*args, **kwargs)
337233
return result
234+
338235
return _wrapper
339236

340237

@@ -382,6 +279,7 @@ def out_function(function):
382279
@function_wrapper
383280
def _wrapper(wrapped, instance, args, kwargs):
384281
return function(wrapped(*args, **kwargs))
282+
385283
return _wrapper
386284

387285

newrelic/packages/wrapt/__init__.py

+7-4
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
1-
__version_info__ = ('1', '14', '1')
1+
__version_info__ = ('1', '16', '0')
22
__version__ = '.'.join(__version_info__)
33

4-
from .wrappers import (ObjectProxy, CallableObjectProxy, FunctionWrapper,
5-
BoundFunctionWrapper, WeakFunctionProxy, PartialCallableObjectProxy,
6-
resolve_path, apply_patch, wrap_object, wrap_object_attribute,
4+
from .__wrapt__ import (ObjectProxy, CallableObjectProxy, FunctionWrapper,
5+
BoundFunctionWrapper, PartialCallableObjectProxy)
6+
7+
from .patches import (resolve_path, apply_patch, wrap_object, wrap_object_attribute,
78
function_wrapper, wrap_function_wrapper, patch_function_wrapper,
89
transient_function_wrapper)
910

11+
from .weakrefs import WeakFunctionProxy
12+
1013
from .decorators import (adapter_factory, AdapterFactory, decorator,
1114
synchronized)
1215

newrelic/packages/wrapt/__wrapt__.py

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import os
2+
3+
from .wrappers import (ObjectProxy, CallableObjectProxy,
4+
PartialCallableObjectProxy, FunctionWrapper,
5+
BoundFunctionWrapper, _FunctionWrapperBase)
6+
7+
try:
8+
if not os.environ.get('WRAPT_DISABLE_EXTENSIONS'):
9+
from ._wrappers import (ObjectProxy, CallableObjectProxy,
10+
PartialCallableObjectProxy, FunctionWrapper,
11+
BoundFunctionWrapper, _FunctionWrapperBase)
12+
13+
except ImportError:
14+
pass

0 commit comments

Comments
 (0)