Path: csiph.com!fu-berlin.de!uni-berlin.de!not-for-mail From: Chris Angelico Newsgroups: comp.lang.python Subject: Re: Keeping context-manager object alive through function calls Date: Wed, 11 Nov 2015 09:53:43 +1100 Lines: 52 Message-ID: References: Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 X-Trace: news.uni-berlin.de 4izVfuK246Wp5MnwIaSdVQYw/I0lNsqpds7b23yfA3fA== Return-Path: X-Original-To: python-list@python.org Delivered-To: python-list@mail.python.org X-Spam-Status: OK 0.010 X-Spam-Evidence: '*H*': 0.98; '*S*': 0.00; 'context': 0.05; 'cc:addr :python-list': 0.09; '*is*': 0.09; 'modes': 0.09; 'output': 0.13; 'def': 0.13; 'wed,': 0.15; 'conn': 0.16; 'conn)': 0.16; 'conn):': 0.16; 'contextlib': 0.16; 'depart': 0.16; 'from:addr:rosuav': 0.16; 'from:name:chris angelico': 0.16; 'received:209.85.213.176': 0.16; 'received:io': 0.16; 'received:psf.io': 0.16; 'subject:alive': 0.16; 'subject:object': 0.16; 'wrote:': 0.16; '2015': 0.20; 'cc:2**0': 0.20; 'cc:addr:python.org': 0.20; 'am,': 0.23; 'defined': 0.23; 'this:': 0.23; 'second': 0.24; 'import': 0.24; 'header:In-Reply-To:1': 0.24; 'separate': 0.27; 'question': 0.27; 'message-id:@mail.gmail.com': 0.27; 'data,': 0.27; 'yield': 0.27; 'device': 0.28; 'be:': 0.29; 'forces': 0.29; 'guarantees': 0.29; 'other,': 0.29; 'another': 0.32; 'handle': 0.34; 'skip:d 20': 0.34; 'gets': 0.35; 'received:google.com': 0.35; 'done': 0.35; 'clear': 0.35; 'nov': 0.35; 'but': 0.36; 'there': 0.36; 'received:209.85': 0.36; 'subject:: ': 0.37; 'two': 0.37; 'turn': 0.37; 'received:209.85.213': 0.37; 'received:209': 0.38; 'names': 0.38; 'whatever': 0.39; 'subject:-': 0.39; 'build': 0.40; 'easy': 0.60; 'your': 0.60; 'managers': 0.63; 'benefit': 0.66; 'here': 0.66; 'connection.': 0.76; '9:36': 0.84; 'abandon': 0.84; 'chrisa': 0.84; 'gen': 0.84; 'pablo': 0.84; 'subject:calls': 0.84; 'to:none': 0.91 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=mime-version:in-reply-to:references:date:message-id:subject:from:cc :content-type; bh=7/YAlJE6dkqp3MhX4Jybcv+yJyq3qj4V1xd/v9nENC0=; b=XQtkBv9nf/JlAnfQXR7UmFc1hrVZkydRZbCd7ciaPQv63KhH8UqNpg41rl18S8iQpu pH8BTCNAZ6KOr3ugEzkQHoevPTE3DCiLeDhagROJpXv7MM9uJKc/LUlpvPfBgMcwkOtV mhxCjAk6L1CSOtEbWuUFLCt1/2k07pYUteNwZ1MvJpCug9rAWGILFHIWCNwWEWprujar N+On3XiBoozT0UT7NHgGYLWP8n7IhFL7ZfKllHfK9ZhdSB/wtuycV4EmLzolEfTwsyIr vd4udXDwsQd/eCQkAp3VRgEUie4JoosBKsUUn98EDgpyFeZK6IkNpyPQLEIG4JSOueg8 U7KA== X-Received: by 10.50.83.104 with SMTP id p8mr29380563igy.13.1447196023733; Tue, 10 Nov 2015 14:53:43 -0800 (PST) In-Reply-To: X-BeenThere: python-list@python.org X-Mailman-Version: 2.1.20+ Precedence: list List-Id: General discussion list for the Python programming language List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Xref: csiph.com comp.lang.python:98617 On Wed, Nov 11, 2015 at 9:36 AM, Pablo Lucena wrote: > And calling it as such: > > def do_more_stuff(device): > gen = do_stuff(device, return_handle=True) > data, conn = next(gen) > output = conn.send_command("show users") > #process output... > return processed_output The first question needs to be: When *is* conn to be closed? What you've done here is abandon all of the benefit of the context manager, because it's no longer clear when you're done with the connection. Here's an alternative: Turn your generator into another context manager. from contextlib import contextmanager @contextmanager def do_stuff(device): with manager(device) as conn: output = conn.send_command("show ip route") #process output... yield (processed_output, conn) def do_stuff_now(device): with do_stuff(device) as (out, conn): return out This forces you to separate the return_handle=True and return_handle=False modes into two separate functions, but since one is defined in terms of the other, you don't have a massive maintenance burden. (Backward compatibility might have you call the first one do_stuff_with_handle and the second one gets the name do_stuff. Or whatever names make sense. Handwave.) So now you can use do_stuff_now() the way you were using do_stuff(), and if you want to keep using the handle afterward, you use it like this: def do_more_stuff(device): with do_stuff(device) as (data, conn): output = conn.send_command("show users") #process output... return processed_output This guarantees that the device will be closed as you depart do_more_stuff. There are other ways, but this is an easy way to build context managers on top of context managers. ChrisA