If you attempt to register an extension with psycopg2 and happen to use the newrelic python client library you may get an error that looks like this
File "/opt/balanced/embedded/lib/python2.7/site-packages/psycopg2/_json.py", line 142, in register_default_json
loads=loads, oid=JSON_OID, array_oid=JSONARRAY_OID)
File "/opt/balanced/embedded/lib/python2.7/site-packages/psycopg2/_json.py", line 125, in register_json
register_type(JSON, not globally and conn_or_curs or None)
TypeError: argument 2 must be a connection, cursor or None
The issue you're running into is that newrelic decides to play god and replace the connection object with a proxy that it implements called ConnectionWrapper.
This is a problem because psycopg2 has some code that inspects the type of object in order to determine what how to register the extension.
Luckily SQLAlchemy checks for a handy method called _sqla_unwrap
which can be used to get the underlying connection and pass it to psycopg. I guess the engineers at newrelic didn't get around to testing this scenario and their code includes a bunch of anonymous classes so it's hard to inherit the object and patch it yourself. Here's how you can fix it yourself:
Write some code to patch the connection class
class NewRelicConnectionWrapperProxy(object):
def __init__(self, connect):
self.__connect = connect
def __call__(self, *args, **kwargs):
cxn = self.__connect(*args, **kwargs)
object.__setattr__(cxn, '_sqla_unwrap', cxn._nr_connection)
return cxn
Now, after you initialize the newrelic agent but before you initialize your database connection you can monkey patch everything:
import newrelic.agent
newrelic.agent.initialize(config_file, environment)
import psycopg2
psycopg2.connect = NewRelicConnectionWrapperProxy(psycopg2.connect)
That's all there is to it. I hope this is handy for someone.
To New Relic devs: If you fix this, please do not use anonymous classes, it's really hard to extend your code when you cannot inherit and override a class!
The only way I've found to avoid this issue is to use
register_json()
instead ofregister_type()
. When all arguments are provided it doesn't really have any JSON-specific logic, contrary to what the name may suggest.For example, the following snippet will raise
TypeError: argument 2 must be a connection, cursor or None
when used with Newrelic's agent:
While this snippet won't raise the error and will produce an equivalent result:
I'm not really sure why one works and the other doesn't. The internal implementation of the
register_json
function uses practically the same code as the first snippet, so I'm guessing it's due to some import order shenanigans.