Path: csiph.com!fu-berlin.de!uni-berlin.de!not-for-mail From: Ian Kelly Newsgroups: comp.lang.python Subject: Re: Question about asyncio and blocking operations Date: Wed, 27 Jan 2016 09:15:35 -0700 Lines: 90 Message-ID: References: Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 X-Trace: news.uni-berlin.de BooDT9wfksQX0mGUjaYaZwjqqtZh9nrW+Max5c0D59Ww== Return-Path: X-Original-To: python-list@python.org Delivered-To: python-list@mail.python.org X-Spam-Status: OK 0.001 X-Spam-Evidence: '*H*': 1.00; '*S*': 0.00; 'subject:Question': 0.05; 'attributes': 0.07; 'practice,': 0.07; 'block.': 0.09; 'cursor': 0.09; 'vast': 0.09; 'assume': 0.11; 'jan': 0.11; 'exception': 0.13; 'def': 0.13; 'wed,': 0.15; 'feedback.': 0.15; 'instead.': 0.15; '2016': 0.16; 'async': 0.16; 'conn': 0.16; 'crud': 0.16; 'executor': 0.16; 'expert,': 0.16; 'googling': 0.16; 'ian.': 0.16; 'iterator': 0.16; 'larger,': 0.16; 'received:io': 0.16; 'received:psf.io': 0.16; 'skip:n 70': 0.16; 'sqlite3': 0.16; 'thread.': 0.16; 'wrote:': 0.16; 'attribute': 0.18; 'basically': 0.18; 'suggested': 0.20; 'work,': 0.21; 'commands,': 0.22; 'next,': 0.22; 'trying': 0.22; 'am,': 0.23; 'fit': 0.23; 'bit': 0.23; 'seems': 0.23; 'wrote': 0.23; 'skip:l 40': 0.23; 'this:': 0.23; 'thanks,': 0.24; 'second': 0.24; 'tried': 0.24; 'import': 0.24; 'examples': 0.24; 'header:In-Reply-To:1': 0.24; "doesn't": 0.26; 'define': 0.27; 'message-id:@mail.gmail.com': 0.27; 'operations,': 0.27; 'function': 0.28; "skip:' 10": 0.28; 'looks': 0.29; 'url:peps': 0.29; "i'm": 0.30; 'minimal': 0.30; 'probably': 0.31; 'skip:_ 10': 0.32; 'useful': 0.33; 'class': 0.33; 'url:python': 0.33; 'consist': 0.33; 'right?': 0.33; 'wrap': 0.33; 'schedule': 0.34; 'running': 0.34; 'that,': 0.34; 'received:google.com': 0.35; 'on,': 0.35; 'could': 0.35; 'url:dev': 0.35; 'something': 0.35; 'but': 0.36; 'url:org': 0.36; 'received:209.85': 0.36; 'to:addr:python-list': 0.36; 'subject:: ': 0.37; 'received:209.85.213': 0.37; 'seem': 0.37; 'doing': 0.38; 'creation': 0.38; 'received:209': 0.38; 'self': 0.38; 'why': 0.39; 'sure': 0.39; 'does': 0.39; 'to:addr:python.org': 0.40; 'where': 0.40; 'some': 0.40; 'future': 0.60; 'entire': 0.61; 'provide': 0.61; 'impact': 0.61; 'show': 0.62; 'here.': 0.62; 'times': 0.63; "they're": 0.66; 'decided': 0.66; 'fact,': 0.67; 'frank': 0.72; 'await': 0.76; 'arg1,': 0.84; 'isolate': 0.84; 'odd,': 0.84; 'to:name:python': 0.84; 'working,': 0.84; 'scheduling': 0.91 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=mime-version:in-reply-to:references:from:date:message-id:subject:to :content-type; bh=YXjQPLnaejN/ynSANgugbrMDgDAwl/PS8TmPv6FuBOo=; b=QQtmyc73EreBf9HWZiHq4WgJKY75XaJzh11JurnJMHZUGoImif7xb5V+z56wHYRCpb TERNVMjfwfqZA+v/ypO31jclG6VF1Qiad0Jn7tF4BU4Nu4W0hmQnUmpXSIkOqtMwmZYo UMXon9vUjA4cHjt8JbeKaljyj2jJqCS2ys1NLx3bPFzhvgOUuwT2GN2+SknLzA/+4Vje znxwuSq7Re2e3zVSXfQUwx0Pw8WEmErNXOuew/KFZcuCjc6OvvAhIHhNxx1Ln01T/mEM vznd2nq0yKdPsUX6Vn4W/bb2PwLmb5dYhdPmXnvgLOmMXKNY/yMKosdPveX8kHhA/S+U 7uIg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:mime-version:in-reply-to:references:from:date :message-id:subject:to:content-type; bh=YXjQPLnaejN/ynSANgugbrMDgDAwl/PS8TmPv6FuBOo=; b=eFcCh3MQfaLj+nXpH65IlySYyQSOo1VJ9xwjCS9q7Oyzb01CySGuG9p3UJI7ev9UIC 2omRVi2wNYCxFxRb1Ti1rZ5ytWM2YNv9ZyOqCSj14zC/1UY7A3QVbf0B+TBWsmsOEsJl dV2HKqvytOxi+WE7mA6r1WpA+h7HlBNZFqX1AdvHXvDg27dtKP5BQ21PLQ39E66AxA4e wZdFZSTpeErGIJ2gSWjPdjJf22uXUoHvwAS9Zkxq82+RVHpu/nqOkcCm4rJhehQ/S9TX TEsv61n6GR4X6xoRgpYXDa39LqiuhdOJt+hoZxzLOXwEfsjBoo9Kg/5efSpB+KEV07+g 0rZA== X-Gm-Message-State: AG10YORwHUkE85p1f3jotVlEipj0/VVcE1LOZce/6DgtLp0Q+UbIR3fVqbbX9piw844pBaDriQIS1m9fYux/sg== X-Received: by 10.50.138.76 with SMTP id qo12mr30405059igb.85.1453911375194; Wed, 27 Jan 2016 08:16:15 -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:102162 On Wed, Jan 27, 2016 at 7:40 AM, Frank Millman wrote: > "Ian Kelly" wrote in message > news:CALwzidk-RBkB-vi6CgcEeoFHQrsoTFvqX9MqzDD=rnY5bOCRUg@mail.gmail.com... > >> You probably want an asynchronous iterator here. If the cursor doesn't >> provide that, then you can wrap it in one. In fact, this is basically >> one of the examples in the PEP: >> https://www.python.org/dev/peps/pep-0492/#example-1 >> > > Thanks, Ian. I had a look, and it does seem to fit the bill, but I could not > get it to work, and I am running out of time. > > Specifically, I tried to get it working with the sqlite3 cursor. I am no > expert, but after some googling I tried this - > > import sqlite3 > conn = sqlite3.connect('/sqlite_db') > cur = conn.cursor() > > async def __aiter__(self): > return self > > async def __anext__(self): > loop = asyncio.get_event_loop() > return await loop.run_in_executor(None, self.__next__) > > import types > cur.__aiter__ = types.MethodType( __aiter__, cur ) > cur.__anext__ = types.MethodType( __anext__, cur ) > > It failed with this exception - > > AttributeError: 'sqlite3.Cursor' object has no attribute '__aiter__' > > I think this is what happens if a class uses 'slots' to define its > attributes - it will not permit the creation of a new one. This is why I suggested wrapping the cursor instead. Something like this: class CursorWrapper: def __init__(self, cursor): self._cursor = cursor async def __aiter__(self): return self async def __anext__(self): loop = asyncio.get_event_loop() return await loop.run_in_executor(None, next, self._cursor) > Anyway, moving on, I decided to change tack. Up to now I have been trying to > isolate the function where I actually communicate with the database, and > wrap that in a Future with 'run_in_executor'. > > In practice, the vast majority of my interactions with the database consist > of very small CRUD commands, and will have minimal impact on response times > even if they block. So I decided to focus on a couple of functions which are > larger, and try to wrap the entire function in a Future with > 'run_in_executor'. > > It seems to be working, but it looks a bit odd, so I will show what I am > doing and ask for feedback. > > Assume a slow function - > > async def slow_function(arg1, arg2): > [do stuff] > > It now looks like this - > > async def slow_function(arg1, arg2): > loop = asyncio.get_event_loop() > await loop.run_in_executor(None, slow_function_1, arg1, arg2) > > def slow_function_1(self, arg1, arg2): > loop = asyncio.new_event_loop() > asyncio.set_event_loop(loop) > loop.run_until_complete(slow_function_2(arg1, arg2)) > > async slow_function_2(arg1, arg2): > [do stuff] > > Does this look right? I'm not sure I understand what you're trying to accomplish by running a second event loop inside the executor thread. It will only be useful for scheduling asynchronous operations, and if they're asynchronous then why not schedule them on the original event loop?