0

I am using a decorator to open and close a neo4j database session (and allow my decorated functions to run queries within that session). At first I used a decorator function :

 from neo4j.v1 import GraphDatabase, basic_auth
 def session(func,url="bolt://localhost:7474", user="neo4j",pwd="neo4j", *args, **kwargs):
     def operate(*args, **kwargs):
         driver = GraphDatabase.driver(url, auth=basic_auth(user,pwd))
         session=driver.session()
         kwargs["session"]=session 
         result=func(*args, **kwargs)
         session.close()
         return result
     return operate

and for example I then call this function:

@session
def RUN(command,session):
    result=session.run(command)
    return(result)

However, this opens and closes the session for every query which is resource consuming. Thus, I tried to create a decorator class, and store the session:

class session(object):
    def __init__(self, func, url="bolt://localhost", user="neo4j", pwd="neo4j"):
        try:
            driver = GraphDatabase.driver(url, auth=basic_auth(user, pwd))
            print("session opened")
        except:
            print("Exception during authentification")
            self.__exit__()
        else:
            session=driver.session()
            self.func=func
            self.SESSION=session

    def __call__(self, *args, **kwargs):
        kwargs["session"]=self.SESSION 
        result=self.func(*args, **kwargs)
        return result
    def __del__(self):
        print("del")
        try:
            self.SESSION.close()
            print("session closed")
        except:
            print("could not close session") 

This seems to work, as the "session opened" appears only once. But the session does not seem to close ("session close" is never printed).

So my first question is the following, how can I call the self.SESSION.close() at the destruction of the decorator ?

I'm also wondering if I understood well what my code is doing. When I call a decorated function such as RUN, is only one session object created? And what if I were to have an other decorated function MATCH

@session
def MATCH(*args,**kwargs):
    pass

would the session object be the same ?

Luc M
  • 199
  • 2
  • 13
  • 1
    The class would probably go away when _all_ the functions that it decorates goes away (In this case, it looks like you have one session per function decorated). If the functions are at the module-level, the answer is that the session won't get closed until the interpreter is shutting down (and _maybe_ not even then). – mgilson Oct 07 '16 at 15:22

1 Answers1

1

How to

Here is a template to create a decorator with parameters using a function:

def decorator_maker(param1, param2):
    print("The parameters of my decorator are: {0} and {1}".format(param1, param2))

    def my_decorator(function_to_decorate):
        def wrapper(arg1, arg2):
            print("before call")
            result = function_to_decorate(arg1, arg2)
            print("after call")
            return result

        return wrapper

    return my_decorator

The usage is as follow:

@decorator_maker("hello", "How are you?")
def my_function(arg1, arg2):
    print("The parameters of my function are: {0} and {1}".format(arg1, arg2))
    return arg1 + "-" + arg2

With a class, it is more straightforward:

class decorator_maker(object):
    def __init__(self, param1, param2):
        print("The parameters of my decorator are: {0} and {1}".format(param1, param2))
        self.param1 = param1
        self.param2 = param2

    def __call__(self, function_to_decorate):
        def wrapper(arg1, arg2):
            print("before call")
            result = function_to_decorate(arg1, arg2)
            print("after call")
            return result

        return wrapper

Answer

To open and close your session before/after the function call, you have to implement it in the wrapped function, like this:

class with_session(object):
    def __init__(self, url="bolt://localhost", user="neo4j", pwd="neo4j"):
        self.url = url
        self.user = user
        self.pwd = pwd

    def __call__(self, f):
        def wrapper(*args, **kwargs):
            driver = GraphDatabase.driver(self.url, auth=basic_auth(self.user, self.pwd))
            kwargs["session"] = session = driver.session()
            try:
                return f(*args, **kwargs)
            finally:
                session.close()

        return wrapper

Notes:

  • You can let the exception raises...
  • You should rename the decorator (like I did) to avoid shadowing the name "session".
  • This decorator is not a a context manager. For that you need to use __enter__ and __exit__, this is another use case...

Reference

See: How to make a chain of function decorators?

Community
  • 1
  • 1
Laurent LAPORTE
  • 21,958
  • 6
  • 58
  • 103