Path: csiph.com!v102.xanadu-bbs.net!xanadu-bbs.net!goblin3!goblin1!goblin.stu.neva.ru!news.osn.de!diablo2.news.osn.de!news.tele.dk!news.tele.dk!small.news.tele.dk!newsgate.cistron.nl!newsgate.news.xs4all.nl!post.news.xs4all.nl!not-for-mail Return-Path: X-Original-To: python-list@python.org Delivered-To: python-list@mail.python.org X-Spam-Status: OK 0.000 X-Spam-Evidence: '*H*': 1.00; '*S*': 0.00; ';-)': 0.03; 'handler': 0.05; '(using': 0.07; 'great.': 0.07; 'initialize': 0.07; 'rename': 0.07; '__init__': 0.09; 'callback': 0.09; 'constructor': 0.09; 'method,': 0.09; 'parameter': 0.09; 'pep': 0.09; 'received:80.91': 0.09; 'received:80.91.229': 0.09; 'received:gmane.org': 0.09; 'received:list': 0.09; 'solution,': 0.09; 'try:': 0.09; 'useless': 0.09; 'python': 0.11; 'def': 0.12; 'jan': 0.12; 'changes': 0.15; 'windows': 0.15; 'above?': 0.16; 'api,': 0.16; 'func': 0.16; "guido's": 0.16; 'handler.': 0.16; 'handlers.': 0.16; 'itself,': 0.16; 'magic': 0.16; 'received:80.91.229.3': 0.16; 'received:plane.gmane.org': 0.16; 'reedy': 0.16; 'stuff,': 0.16; 'tasks,': 0.16; 'url:peps': 0.16; 'write.': 0.16; 'wrote:': 0.18; 'library': 0.18; 'import': 0.22; 'manual': 0.22; 'header:User-Agent:1': 0.23; 'either.': 0.24; 'manager.': 0.24; 'url:dev': 0.24; '(or': 0.24; 'task': 0.26; 'second': 0.26; '(for': 0.26; 'header:X-Complaints-To:1': 0.27; 'header:In-Reply-To:1': 0.27; 'tried': 0.27; 'function': 0.29; 'specified': 0.30; 'especially': 0.30; 'code': 0.31; 'easier': 0.31; 'class': 0.32; 'stuff': 0.32; 'interface': 0.32; 'run': 0.32; 'another': 0.32; 'url:python': 0.33; '(e.g.': 0.33; 'call.': 0.33; 'implemented': 0.33; 'comment': 0.34; 'skip:_ 10': 0.34; 'could': 0.34; 'subject:with': 0.35; 'except': 0.35; 'skip:s 30': 0.35; 'but': 0.35; 'there': 0.35; 'done': 0.36; 'entry': 0.36; 'method': 0.36; 'url:org': 0.36; 'should': 0.36; 'seconds': 0.37; 'too': 0.37; 'two': 0.37; 'implement': 0.38; 'version,': 0.38; 'to:addr:python-list': 0.38; 'pm,': 0.38; 'anything': 0.39; 'does': 0.39; 'to:addr:python.org': 0.39; 'skip:p 20': 0.39; 'received:org': 0.40; 'called': 0.40; 'how': 0.40; 'easy': 0.60; 'skip:a 30': 0.61; 'new': 0.61; 'received:173': 0.61; 'first': 0.61; 'back': 0.62; 'kind': 0.63; 'such': 0.63; 'provide': 0.64; 'more': 0.64; 'different': 0.65; 'here': 0.66; 'believe': 0.68; 'periodically': 0.68; 'subject': 0.69; 'containing': 0.69; 'repeat': 0.74; 'subject.': 0.74; 'behavior': 0.77; 'received:fios.verizon.net': 0.84; 'self.run()': 0.84; 'usage.': 0.84; 'do:': 0.91 X-Injected-Via-Gmane: http://gmane.org/ To: python-list@python.org From: Terry Reedy Subject: Re: Periodic execution with asyncio Date: Fri, 22 Nov 2013 19:00:17 -0500 References: <528FCCF2.4020608@tobix.eu> Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: 7bit X-Gmane-NNTP-Posting-Host: pool-173-75-254-207.phlapa.fios.verizon.net User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:24.0) Gecko/20100101 Thunderbird/24.1.1 In-Reply-To: <528FCCF2.4020608@tobix.eu> X-BeenThere: python-list@python.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: General discussion list for the Python programming language List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Newsgroups: comp.lang.python Message-ID: Lines: 115 NNTP-Posting-Host: 2001:888:2000:d::a6 X-Trace: 1385164832 news.xs4all.nl 15876 [2001:888:2000:d::a6]:53075 X-Complaints-To: abuse@xs4all.nl Xref: csiph.com comp.lang.python:60255 On 11/22/2013 4:30 PM, Tobias M. wrote: > I am using the asyncio package (Codename 'Tulip'), which will be > available in Python 3.4, for the first time. Great. New stuff, both behavior and API, needs to be 'exercised', especially by non-experts in the subject. I have no experience with async stuff either. > I want the event loop to run a function periodically (e.g. every 2 > seconds). PEP 3156 suggests two ways to implement such a periodic call: The PEP is still the doc, and the Library Manual entry is a stub pointing back to it. http://python.org/dev/peps/pep-3156/ > 1. Using a callback that reschedules itself, using call_later(). > 2. Using a coroutine containing a loop and a sleep() call. > > I implemented the first approach in a class with an easy to use > interface. It can be subclassed and the run() method > can be overwritten to provide the code that will be called periodically. > The interval is specified in the constructor and the task can be started > and stopped: > > import asyncio > > class PeriodicTask(object): > > def __init__(self, interval): > self._interval = interval > self._loop = asyncio.get_event_loop() > > def _run(self): > self.run() > self._handler = self._loop.call_later(self._interval, self._run) > > def run(self): > print('Hello World') > > def start(self): > self._handler = self._loop.call_later(self._interval, self._run) > > def stop(self): > self._handler.cancel() > > > To run this task and execute run() every 2 seconds you can do: > > task = PeriodicTask(2) > task.start() > asyncio.get_event_loop().run_forever() > 1. Is this a reasonable implementation or is there anything to improve? It may be both reasonable and subject to change. Here is my version, with the following changes * Make the task function a parameter 'func'. * Rename start to _set to better describe what is does and call it in the _run function to not repeat the handler setting line. (If 'start' has any magic meaning, I am not aware of it. I am aware that there could be situations in which one would want a public method, to be called from other handlers. But that would be a different class ;-) * Fully initialize the instance in __init__ by calling _set. * Provide a clean way to stop the event loop without a traceback (or using Windows Task Manager. * Comment out the stop method because it is useless in the current usage. I believe handler.cancel is only relevant after the handler is set and before it is fired. This can only be done from another handler. import asyncio class PeriodicTask(object): def __init__(self, func, interval): self.func = func self.interval = interval self._loop = asyncio.get_event_loop() self._set() def _set(self): self._handler = self._loop.call_later(self.interval, self._run) def _run(self): self.func() self._set() ## def stop(self): ## self._handler.cancel() def f(): print('Hello World') task = PeriodicTask(f, 2) try: asyncio.get_event_loop().run_forever() except KeyboardInterrupt: print('Loop stopped') > 2. How would you implement the second approach from the PEP (using a > coroutine) with the same interface as my PeriodicTask above? Theoretically, by Guido's rationale, that should be easier (for me, at least), since it would be more like the kind of Python code I already write. > I tried the second approach but wasn't able to come up with a solution, > as I was too confused by the concepts of coroutines, Tasks, etc. I will try to look at the PEP and see how it works for me. -- Terry Jan Reedy