How do I write a decorator to wrap something in a context manager, that takes parameters?
Asked Answered
M

1

9

I've seen How to use a context manager inside a decorator and how to pass an object created in decorator to decorated function as well as python decorators with parameters, and I'm trying to combine the two..but I'm struggling to get my head round it.

I'd much rather use the func tools @wrap decorator to do this if possible, as I know if will preserve the doc string.

What I want to do is this:

def pyro_opener(func,service,database,port,secret_key):
    def wrapper(params):
        with Pyro4.Proxy("PYRO:"+service+"@"+database+":"+port) as obj:
            obj.set_secret_key(secret_key)
            return obj.func(params)
    return wrapper


@pyro_opener(output_service, employee_db,port=9876,secret_key="h3llow0rld")
def get_employee_names(salary):
    return obj.return_employee_names(salary)  # obj is clearly not in scope here
                                              # but what else can I do?


get_employee_names(25000)

>>>> Bob, Jane, Mary

I don't think this works this way, the method return_employee_names is on the service at the other end of the connection. Should I just return the function call? If so how do I pass the params in then?

Michaud answered 27/5, 2015 at 17:1 Comment(0)
B
8

You'd pass in the object bound to with with ... as to the wrapped function; the function would have to accept such an argument.

This is analogous to how methods work; they are just functions with an extra first argument (self) passed in:

def pyro_opener(service, database, port, secret_key):
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kw):
            with Pyro4.Proxy("PYRO:{}@{}:{}".format(service, database, port)) as obj:
                obj.set_secret_key(secret_key)
                return func(obj, *args, **kw)
        return wrapper
    retutrn decorator

@pyro_opener(output_service, employee_db, port=9876, secret_key="h3llow0rld")
def get_employee_names(obj, salary):
    return obj.return_employee_names(salary)

Note that I had to add another nested function in the pyro_opener() to make it a proper decorator factory.

Bedlam answered 27/5, 2015 at 17:6 Comment(6)
So when I call get_employee_names where do I get the obj param from? Also, I thought that using @wraps mean I wouldn't need the extra level of decoratoryness...?Michaud
@Pureferret: look closely at my version of get_employee_names(); the obj argument is explicit. functools.wraps() has nothing to do with decorator factories, and everything with copying metadata across from the wrapped function to the wrapper, so that the latter looks more like the original.Bedlam
@martjinpieters what I mean is, when I want to use ` get_employee_names` I need to supply the obj parameters (just line I need to supply salary). I can't as ñ call the method like so get_employee_names(???,300000) but what do I put in for the ??? ?Michaud
You don't; the decorator wrapper passes in that argument. Just like calling a method on an instance, where self is supplied for you by Python.Bedlam
Look closely at how wrapper() calls func().Bedlam
Right, good analogy with self. I would have called that... Implicit, I guess? I'm used to all parameters for a method coming from the same place....except self...thanks. once I've tested it tomorrow I'll check the accept mark.Michaud

© 2022 - 2024 — McMap. All rights reserved.