Shuman Shuman - 11 days ago 7
Python Question

How to update argument passed to context manager during code execution?

node
is passed to the context manager, which I want to lock at the beginning, then do some work, at last unlock the node. But if
node
is changed during the execution inside the with clause, how do I pass the updated
node
to
unlockFunc
?

This code runs in python 2.7

from contextlib import contextmanager


@contextmanager
def call(begin, end, *args, **kwargs):
begin(*args, **kwargs)
try:
yield
finally:
end(*args, **kwargs)


def lockFunc(*args):
print 'in lockFunc'
print 'lock %s' %args[0]


def unlockFunc(*args):
print 'in unlockFunc'
print 'unlock %s' %args[0]


node = 'old-node'
with call(lockFunc, unlockFunc, node):

print 'in with'
# update node value here
node = 'new-node'


But the output is

in lockFunc
lock old-node
in with
in unlockFunc
unlock old-node


How can I let
unlockFunc
know that
node
has been changed.

edit: I have tried making
node
a list, but that does not work.

Answer

The problem is that node = 'new-node' doesn't change the object that the contextmanager has a reference to; it changes what the identifier node refers to.

You need instead to tell the object to change, and you can only do this with a mutable object; strings are not mutable. A list would work, but I think a class is probably a bit clearer:

from contextlib import contextmanager

class Node(object):
    def __init__(self,value):
        self.value=value

    def update(self, newvalue):
        self.value = newvalue

    def __str__(self):
        return str(self.value)

@contextmanager
def call(begin, end, *args, **kwargs):
    begin(*args, **kwargs)
    try:
        yield
    finally:
        end(*args, **kwargs)


def lockFunc(*args):
    print 'in lockFunc'
    print 'lock %s' %args[0]


def unlockFunc(*args):
    print 'in unlockFunc'
    print 'unlock %s' %args[0]


node = Node('old-node')
with call(lockFunc, unlockFunc, node):

    print 'in with'
    # update node value here
    node.update('new-node')

which produces

in lockFunc
lock old-node
in with
in unlockFunc
unlock new-node

(Ubuntu 14.04, Python 2.7.6)