Skip to content

Commit 32c66d8

Browse files
TimPansinomergify[bot]hmstepanek
authored
Deprecate ObjectWrapper API (#996)
* 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 * Unify ObjectWrapper with FunctionWrapper * Remove ObjectWrapper from httplib * Remove ObjectWrapper from tastypie * Replace ObjectWrapper use in console * Remove ObjectWrapper from celery * Remove extra import * Update agent APIs * Deprecate ObjectWrapper * Fix object wrapper imports * More import issues * Fix taskwrapper in celery * Pin last supported flask restx version for 3.7 * Undo tox changes * Change all api.object_wrapper references to use new locations * Fixup: callable_name import * Fixup: callable_name import --------- Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> Co-authored-by: Hannah Stepanek <[email protected]>
1 parent 27c874c commit 32c66d8

16 files changed

+65
-94
lines changed

newrelic/agent.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
from newrelic.api.application import application_instance as __application
1616
from newrelic.api.application import application_settings as __application_settings
1717
from newrelic.api.application import register_application as __register_application
18-
from newrelic.api.log import NewRelicContextFormatter # noqa
18+
from newrelic.api.log import NewRelicContextFormatter as __NewRelicContextFormatter
1919
from newrelic.api.time_trace import (
2020
add_custom_span_attribute as __add_custom_span_attribute,
2121
)
@@ -177,6 +177,7 @@ def __asgi_application(*args, **kwargs):
177177
from newrelic.common.object_wrapper import FunctionWrapper as __FunctionWrapper
178178
from newrelic.common.object_wrapper import InFunctionWrapper as __InFunctionWrapper
179179
from newrelic.common.object_wrapper import ObjectProxy as __ObjectProxy
180+
from newrelic.common.object_wrapper import CallableObjectProxy as __CallableObjectProxy
180181
from newrelic.common.object_wrapper import ObjectWrapper as __ObjectWrapper
181182
from newrelic.common.object_wrapper import OutFunctionWrapper as __OutFunctionWrapper
182183
from newrelic.common.object_wrapper import PostFunctionWrapper as __PostFunctionWrapper
@@ -276,6 +277,7 @@ def __asgi_application(*args, **kwargs):
276277
wrap_background_task = __wrap_api_call(__wrap_background_task, "wrap_background_task")
277278
LambdaHandlerWrapper = __wrap_api_call(__LambdaHandlerWrapper, "LambdaHandlerWrapper")
278279
lambda_handler = __wrap_api_call(__lambda_handler, "lambda_handler")
280+
NewRelicContextFormatter = __wrap_api_call(__NewRelicContextFormatter, "NewRelicContextFormatter")
279281
transaction_name = __wrap_api_call(__transaction_name, "transaction_name")
280282
TransactionNameWrapper = __wrap_api_call(__TransactionNameWrapper, "TransactionNameWrapper")
281283
wrap_transaction_name = __wrap_api_call(__wrap_transaction_name, "wrap_transaction_name")
@@ -316,6 +318,7 @@ def __asgi_application(*args, **kwargs):
316318
wrap_message_transaction = __wrap_api_call(__wrap_message_transaction, "wrap_message_transaction")
317319
callable_name = __wrap_api_call(__callable_name, "callable_name")
318320
ObjectProxy = __wrap_api_call(__ObjectProxy, "ObjectProxy")
321+
CallableObjectProxy = __wrap_api_call(__CallableObjectProxy, "CallableObjectProxy")
319322
wrap_object = __wrap_api_call(__wrap_object, "wrap_object")
320323
wrap_object_attribute = __wrap_api_call(__wrap_object_attribute, "wrap_object_attribute")
321324
resolve_path = __wrap_api_call(__resolve_path, "resolve_path")

newrelic/api/solr_trace.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
import newrelic.api.object_wrapper
1616
import newrelic.api.time_trace
17+
import newrelic.common.object_wrapper
1718
import newrelic.core.solr_node
1819

1920

@@ -111,4 +112,4 @@ def decorator(wrapped):
111112

112113

113114
def wrap_solr_trace(module, object_path, library, command):
114-
newrelic.api.object_wrapper.wrap_object(module, object_path, SolrTraceWrapper, (library, command))
115+
newrelic.common.object_wrapper.wrap_object(module, object_path, SolrTraceWrapper, (library, command))

newrelic/common/object_wrapper.py

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
"""
2121

2222
import inspect
23+
import warnings
2324

2425
from newrelic.packages.wrapt import BoundFunctionWrapper as _BoundFunctionWrapper
2526
from newrelic.packages.wrapt import CallableObjectProxy as _CallableObjectProxy
@@ -31,7 +32,6 @@
3132
wrap_object,
3233
wrap_object_attribute,
3334
)
34-
from newrelic.packages.wrapt.__wrapt__ import _FunctionWrapperBase
3535

3636
# We previously had our own pure Python implementation of the generic
3737
# object wrapper but we now defer to using the wrapt module as its C
@@ -122,19 +122,13 @@ class CallableObjectProxy(ObjectProxy, _CallableObjectProxy):
122122
# own code no longer uses it. It reaches down into what are wrapt internals
123123
# at present which shouldn't be doing.
124124

125-
126-
class ObjectWrapper(ObjectProxy, _FunctionWrapperBase):
127-
__bound_function_wrapper__ = _NRBoundFunctionWrapper
128-
125+
class ObjectWrapper(FunctionWrapper):
129126
def __init__(self, wrapped, instance, wrapper):
130-
if isinstance(wrapped, classmethod):
131-
binding = "classmethod"
132-
elif isinstance(wrapped, staticmethod):
133-
binding = "staticmethod"
134-
else:
135-
binding = "function"
136-
137-
super(ObjectWrapper, self).__init__(wrapped, instance, wrapper, binding=binding)
127+
warnings.warn(
128+
("The ObjectWrapper API is deprecated. Please use one of ObjectProxy, FunctionWrapper, or CallableObjectProxy instead."),
129+
DeprecationWarning,
130+
)
131+
super(ObjectWrapper, self).__init__(wrapped, wrapper)
138132

139133

140134
# Function for creating a decorator for applying to functions, as well as

newrelic/config.py

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
import newrelic.api.generator_trace
3535
import newrelic.api.import_hook
3636
import newrelic.api.memcache_trace
37-
import newrelic.api.object_wrapper
37+
from newrelic.common.object_names import callable_name
3838
import newrelic.api.profile_trace
3939
import newrelic.api.settings
4040
import newrelic.api.transaction_name
@@ -1345,7 +1345,7 @@ def _process_background_task_configuration():
13451345
group = _config_object.get(section, "group")
13461346

13471347
if name and name.startswith("lambda "):
1348-
callable_vars = {"callable_name": newrelic.api.object_wrapper.callable_name}
1348+
callable_vars = {"callable_name": callable_name}
13491349
name = eval(name, callable_vars) # nosec, pylint: disable=W0123
13501350

13511351
_logger.debug("register background-task %s", ((module, object_path, application, name, group),))
@@ -1395,7 +1395,7 @@ def _process_database_trace_configuration():
13951395
sql = _config_object.get(section, "sql")
13961396

13971397
if sql.startswith("lambda "):
1398-
callable_vars = {"callable_name": newrelic.api.object_wrapper.callable_name}
1398+
callable_vars = {"callable_name": callable_name}
13991399
sql = eval(sql, callable_vars) # nosec, pylint: disable=W0123
14001400

14011401
_logger.debug("register database-trace %s", ((module, object_path, sql),))
@@ -1450,11 +1450,11 @@ def _process_external_trace_configuration():
14501450
method = _config_object.get(section, "method")
14511451

14521452
if url.startswith("lambda "):
1453-
callable_vars = {"callable_name": newrelic.api.object_wrapper.callable_name}
1453+
callable_vars = {"callable_name": callable_name}
14541454
url = eval(url, callable_vars) # nosec, pylint: disable=W0123
14551455

14561456
if method and method.startswith("lambda "):
1457-
callable_vars = {"callable_name": newrelic.api.object_wrapper.callable_name}
1457+
callable_vars = {"callable_name": callable_name}
14581458
method = eval(method, callable_vars) # nosec, pylint: disable=W0123
14591459

14601460
_logger.debug("register external-trace %s", ((module, object_path, library, url, method),))
@@ -1522,7 +1522,7 @@ def _process_function_trace_configuration():
15221522
rollup = _config_object.get(section, "rollup")
15231523

15241524
if name and name.startswith("lambda "):
1525-
callable_vars = {"callable_name": newrelic.api.object_wrapper.callable_name}
1525+
callable_vars = {"callable_name": callable_name}
15261526
name = eval(name, callable_vars) # nosec, pylint: disable=W0123
15271527

15281528
_logger.debug(
@@ -1580,7 +1580,7 @@ def _process_generator_trace_configuration():
15801580
group = _config_object.get(section, "group")
15811581

15821582
if name and name.startswith("lambda "):
1583-
callable_vars = {"callable_name": newrelic.api.object_wrapper.callable_name}
1583+
callable_vars = {"callable_name": callable_name}
15841584
name = eval(name, callable_vars) # nosec, pylint: disable=W0123
15851585

15861586
_logger.debug("register generator-trace %s", ((module, object_path, name, group),))
@@ -1639,7 +1639,7 @@ def _process_profile_trace_configuration():
16391639
depth = _config_object.get(section, "depth")
16401640

16411641
if name and name.startswith("lambda "):
1642-
callable_vars = {"callable_name": newrelic.api.object_wrapper.callable_name}
1642+
callable_vars = {"callable_name": callable_name}
16431643
name = eval(name, callable_vars) # nosec, pylint: disable=W0123
16441644

16451645
_logger.debug("register profile-trace %s", ((module, object_path, name, group, depth),))
@@ -1689,7 +1689,7 @@ def _process_memcache_trace_configuration():
16891689
command = _config_object.get(section, "command")
16901690

16911691
if command.startswith("lambda "):
1692-
callable_vars = {"callable_name": newrelic.api.object_wrapper.callable_name}
1692+
callable_vars = {"callable_name": callable_name}
16931693
command = eval(command, callable_vars) # nosec, pylint: disable=W0123
16941694

16951695
_logger.debug("register memcache-trace %s", (module, object_path, command))
@@ -1749,7 +1749,7 @@ def _process_transaction_name_configuration():
17491749
priority = _config_object.getint(section, "priority")
17501750

17511751
if name and name.startswith("lambda "):
1752-
callable_vars = {"callable_name": newrelic.api.object_wrapper.callable_name}
1752+
callable_vars = {"callable_name": callable_name}
17531753
name = eval(name, callable_vars) # nosec, pylint: disable=W0123
17541754

17551755
_logger.debug("register transaction-name %s", ((module, object_path, name, group, priority),))

newrelic/console.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,7 @@ def doc_signature(func):
7272
return formatargspec(args[1:], varargs, keywords, defaults)
7373

7474

75-
from newrelic.api.object_wrapper import ObjectWrapper
76-
from newrelic.api.transaction import Transaction
75+
from newrelic.common.object_wrapper import ObjectProxy
7776
from newrelic.core.agent import agent_instance
7877
from newrelic.core.config import flatten_settings, global_settings
7978
from newrelic.core.trace_cache import trace_cache
@@ -161,7 +160,7 @@ def __call__(self, code=None):
161160
__builtin__.exit = Quitter("exit")
162161

163162

164-
class OutputWrapper(ObjectWrapper):
163+
class OutputWrapper(ObjectProxy):
165164
def flush(self):
166165
try:
167166
shell = _consoles.active
@@ -187,8 +186,8 @@ def writelines(self, data):
187186
def intercept_console():
188187
setquit()
189188

190-
sys.stdout = OutputWrapper(sys.stdout, None, None)
191-
sys.stderr = OutputWrapper(sys.stderr, None, None)
189+
sys.stdout = OutputWrapper(sys.stdout)
190+
sys.stderr = OutputWrapper(sys.stderr)
192191

193192

194193
class EmbeddedConsole(code.InteractiveConsole):

newrelic/core/internal_metrics.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import types
1818
import time
1919
import threading
20+
import newrelic.common.object_wrapper
2021

2122
_context = threading.local()
2223

@@ -88,7 +89,7 @@ def decorator(wrapped):
8889
return decorator
8990

9091
def wrap_internal_trace(module, object_path, name=None):
91-
newrelic.api.object_wrapper.wrap_object(module, object_path,
92+
newrelic.common.object_wrapper.wrap_object(module, object_path,
9293
InternalTraceWrapper, (name,))
9394

9495
def internal_metric(name, value):

newrelic/hooks/application_celery.py

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@
2626
from newrelic.api.background_task import BackgroundTask
2727
from newrelic.api.function_trace import FunctionTrace
2828
from newrelic.api.pre_function import wrap_pre_function
29-
from newrelic.api.object_wrapper import callable_name, ObjectWrapper
29+
from newrelic.common.object_names import callable_name
30+
from newrelic.common.object_wrapper import FunctionWrapper
3031
from newrelic.api.transaction import current_transaction
3132
from newrelic.core.agent import shutdown_agent
3233

@@ -98,10 +99,6 @@ def _application():
9899
with BackgroundTask(_application(), _name, 'Celery', source=instance):
99100
return wrapped(*args, **kwargs)
100101

101-
# Start Hotfix v2.2.1.
102-
# obj = ObjectWrapper(wrapped, None, wrapper)
103-
# End Hotfix v2.2.1.
104-
105102
# Celery tasks that inherit from celery.app.task must implement a run()
106103
# method.
107104
# ref: (http://docs.celeryproject.org/en/2.5/reference/
@@ -110,29 +107,23 @@ def _application():
110107
# task. But celery does a micro-optimization where if the __call__ method
111108
# was not overridden by an inherited task, then it will directly execute
112109
# the run() method without going through the __call__ method. Our
113-
# instrumentation via ObjectWrapper() relies on __call__ being called which
110+
# instrumentation via FunctionWrapper() relies on __call__ being called which
114111
# in turn executes the wrapper() function defined above. Since the micro
115112
# optimization bypasses __call__ method it breaks our instrumentation of
116113
# celery. To circumvent this problem, we added a run() attribute to our
117-
# ObjectWrapper which points to our __call__ method. This causes Celery
114+
# FunctionWrapper which points to our __call__ method. This causes Celery
118115
# to execute our __call__ method which in turn applies the wrapper
119116
# correctly before executing the task.
120117
#
121118
# This is only a problem in Celery versions 2.5.3 to 2.5.5. The later
122119
# versions included a monkey-patching provision which did not perform this
123120
# optimization on functions that were monkey-patched.
124121

125-
# Start Hotfix v2.2.1.
126-
# obj.__dict__['run'] = obj.__call__
127-
128-
class _ObjectWrapper(ObjectWrapper):
122+
class TaskWrapper(FunctionWrapper):
129123
def run(self, *args, **kwargs):
130124
return self.__call__(*args, **kwargs)
131125

132-
obj = _ObjectWrapper(wrapped, None, wrapper)
133-
# End Hotfix v2.2.1.
134-
135-
return obj
126+
return TaskWrapper(wrapped, wrapper)
136127

137128

138129
def instrument_celery_app_task(module):

newrelic/hooks/component_piston.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,15 @@
1616

1717
import newrelic.api.transaction
1818
import newrelic.api.function_trace
19-
import newrelic.api.object_wrapper
19+
import newrelic.common.object_wrapper
20+
from newrelic.common.object_names import callable_name
2021
import newrelic.api.in_function
2122

2223

2324
class MethodWrapper(object):
2425

2526
def __init__(self, wrapped, priority=None):
26-
self._nr_name = newrelic.api.object_wrapper.callable_name(wrapped)
27+
self._nr_name = callable_name(wrapped)
2728
self._nr_wrapped = wrapped
2829
self._nr_priority = priority
2930

@@ -76,7 +77,7 @@ def __call__(self, *args, **kwargs):
7677

7778
def instrument_piston_resource(module):
7879

79-
newrelic.api.object_wrapper.wrap_object(module,
80+
newrelic.common.object_wrapper.wrap_object(module,
8081
'Resource.__init__', ResourceInitWrapper)
8182

8283

newrelic/hooks/component_tastypie.py

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,11 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15-
import sys
16-
1715
from newrelic.api.function_trace import FunctionTraceWrapper
18-
from newrelic.api.object_wrapper import ObjectWrapper, callable_name
16+
from newrelic.common.object_names import callable_name
17+
from newrelic.common.object_wrapper import wrap_function_wrapper, function_wrapper
1918
from newrelic.api.transaction import current_transaction
2019
from newrelic.api.time_trace import notice_error
21-
from newrelic.common.object_wrapper import wrap_function_wrapper
2220

2321

2422
def _nr_wrap_handle_exception(wrapped, instance, args, kwargs):
@@ -56,6 +54,7 @@ def outer_fn_wrapper(outer_fn, instance, args, kwargs):
5654
name = callable_name(callback)
5755
group = None
5856

57+
@function_wrapper
5958
def inner_fn_wrapper(inner_fn, instance, args, kwargs):
6059
transaction = current_transaction()
6160

@@ -69,18 +68,14 @@ def inner_fn_wrapper(inner_fn, instance, args, kwargs):
6968

7069
result = outer_fn(*args, **kwargs)
7170

72-
return ObjectWrapper(result, None, inner_fn_wrapper)
71+
return inner_fn_wrapper(result)
7372

7473

7574
def instrument_tastypie_resources(module):
76-
_wrap_view = module.Resource.wrap_view
77-
module.Resource.wrap_view = ObjectWrapper(
78-
_wrap_view, None, outer_fn_wrapper)
75+
wrap_function_wrapper(module, "Resource.wrap_view", outer_fn_wrapper)
7976

80-
wrap_function_wrapper(module, 'Resource._handle_500',
81-
_nr_wrap_handle_exception)
77+
wrap_function_wrapper(module, 'Resource._handle_500', _nr_wrap_handle_exception)
8278

8379

8480
def instrument_tastypie_api(module):
85-
_wrap_view = module.Api.wrap_view
86-
module.Api.wrap_view = ObjectWrapper(_wrap_view, None, outer_fn_wrapper)
81+
wrap_function_wrapper(module, "Api.wrap_view", outer_fn_wrapper)

newrelic/hooks/external_feedparser.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
import newrelic.api.transaction
2121
import newrelic.api.object_wrapper
22+
import newrelic.common.object_wrapper
2223
import newrelic.api.external_trace
2324

2425
class capture_external_trace(object):
@@ -70,5 +71,5 @@ def __getattr__(self, name):
7071
return getattr(self._nr_next_object, name)
7172

7273
def instrument(module):
73-
newrelic.api.object_wrapper.wrap_object(
74+
newrelic.common.object_wrapper.wrap_object(
7475
module, 'parse', capture_external_trace)

0 commit comments

Comments
 (0)