Kannan Ekanath
Kannan Ekanath

Reputation: 17601

Python handling parameters in "with" context

Let us say I have a python method to get last synced changelist in p4. (The question itself is not related to p4 but only a basic python question)

def get_last_synced_changelist(p4port, client_name, p4 = None):
    with P4Connection(p4port) as p4:
        last_change_list = p4.run_changes("-m1", "@%s" % client_name)
        if last_change_list:
            return last_change_list[0]["change"]
        else:
            return None

The caller of this method can either supply p4port which is a String or they could supply the p4 object in itself. My requirement is that if a "p4" object is supplied I want to run the method body without the with context, i.e, I dont want the enter or exit method called on the p4. This is because the responsibility of closing/entering p4 object lies now with the caller. If p4 object is not supplied the with syntax needs to be constructed in this program using the p4port string.

Can someone tell me what is the best way of structuring this method? I want to change the body as less as possible. Basically something like

p4 or with P4Connection(p4port) as p4:
   p4.run_changes(...)

But I am not sure what the best syntax is.

Upvotes: 1

Views: 167

Answers (3)

Blckknght
Blckknght

Reputation: 104712

In Python 2 you'll probably want to create your own context manager, either a dummy one as @glglgl shows in his answer, or perhaps one that wraps up the optional creation of the P4Connection:

import contextlib

@contextlib.context_manager
def p4_context(p4port, p4=None):
    p4 is None:
        with P4Connection(p4port) as p4:
            yield p4
    else:
        yield p4

def get_last_synced_changelist(p4port, client_name, p4 = None):
    with p4_context(p4port, p4) as p4:
        last_change_list = p4.run_changes("-m1", "@%s" % client_name)
        if last_change_list:
            return last_change_list[0]["change"]
        else:
            return None

If you were using Python 3.3 or later, you could instead use the awesome new contextlib.ExitStack class:

import contextlib

def get_last_synced_changelist(p4port, client_name, p4 = None):
    with contextlib.ExitStack() as stack:
        if p4 is None:
            p4 = stack.enter_context(P4Connection(p4port))

        last_change_list = p4.run_changes("-m1", "@%s" % client_name)
        if last_change_list:
            return last_change_list[0]["change"]
        else:
            return None

Upvotes: 0

glglgl
glglgl

Reputation: 91017

You can create a dummy context manager:

import contextlib
@contextlib.contextmanager
def dummyctx(x):
    yield x

def get_last_synced_changelist(p4port, client_name, p4=None):
    if p4 is None:
        ctx = P4Connection(p4port)
    else:
        ctx = dummyctx(p4)
    with ctx as p4:
        last_change_list = p4.run_changes("-m1", "@%s" % client_name)
        if last_change_list:
            return last_change_list[0]["change"]
        else:
            return None

Upvotes: 2

user395760
user395760

Reputation:

It's not directly possible, with is a compound statement and can't be embedded in expressions like this. But you can make use of the fact that your function also supports "borrowing" a resource:

def get_last_synced_changelist(p4port, client_name, p4 = None):
    if p4 is None:
        with P4Connection(p4port) as p4:
            return get_last_synced_changelist(p4port, client_name, p4)
    last_change_list = p4.run_changes("-m1", "@%s" % client_name)
    if last_change_list:
        return last_change_list[0]["change"]
    else:
        return None

This approach even works if you have separate functions for the two paths (which may make sense in this example, as p4port is apparently not used when an existing P4Connection is passed in).

Upvotes: 2

Related Questions