├── __init__.py ├── queues ├── __init__.py ├── resources.md ├── queue1.py └── queue2.py ├── requirements.txt ├── event_loop ├── __init__.py ├── resources.md ├── run_in_executor.py └── demo_text.txt ├── streams ├── __init__.py ├── resources.md ├── tcp_echo_client.py └── tcp_echo_server.py ├── coroutines_and_tasks ├── __init__.py ├── resources.md └── task_cancel.py ├── synchronization_primitives ├── __init__.py ├── resources.md ├── conditions.py ├── event2.py ├── event1.py └── lock1.py ├── .gitignore ├── context_switch.py ├── testfile.py ├── thread_test.py ├── multiprocessing_test.py ├── sync_web_scraping.py ├── future_test.py ├── get_your_ip.py ├── get_your_ip_3.7.py ├── async_web_scraping.py ├── get_your_ip_3.7_error_handling.py ├── github_public_events.py ├── thread_pool_executor.py ├── get_your_ip_timeout.py ├── github_public_events_futures.py ├── process_pool_executor.py ├── generators_and_coroutines.py ├── README.md └── hacker_news_comment_calc.py /__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /queues/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /event_loop/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /streams/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /coroutines_and_tasks/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /synchronization_primitives/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /streams/resources.md: -------------------------------------------------------------------------------- 1 | * https://docs.python.org/3/library/asyncio-stream.html -------------------------------------------------------------------------------- /coroutines_and_tasks/resources.md: -------------------------------------------------------------------------------- 1 | * https://docs.python.org/3/library/asyncio-task.html#task-object 2 | * -------------------------------------------------------------------------------- /queues/resources.md: -------------------------------------------------------------------------------- 1 | * https://pymotw.com/3/asyncio/synchronization.html#queues 2 | * https://docs.python.org/3/library/asyncio-queue.html -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *.sqlite3 3 | *.zip 4 | /.idea/ 5 | /local/ 6 | /ve/ 7 | .swp 8 | *.ipynb 9 | *.log 10 | /just_run_here.py 11 | -------------------------------------------------------------------------------- /event_loop/resources.md: -------------------------------------------------------------------------------- 1 | * https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.loop.run_in_executor 2 | * https://docs.python.org/3/library/asyncio-dev.html#running-blocking-code -------------------------------------------------------------------------------- /context_switch.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | 3 | 4 | async def foo(): 5 | print('Running in foo') 6 | await asyncio.sleep(0) 7 | print('Explicit context switch to foo again') 8 | 9 | 10 | async def bar(): 11 | print('Explicit context to bar') 12 | await asyncio.sleep(0) 13 | print('Implicit context switch back to bar') 14 | 15 | 16 | async def main(): 17 | tasks = [foo(), bar()] 18 | await asyncio.gather(*tasks) 19 | 20 | 21 | asyncio.run(main()) -------------------------------------------------------------------------------- /synchronization_primitives/resources.md: -------------------------------------------------------------------------------- 1 | ### Conditions 2 | * https://pymotw.com/3/asyncio/synchronization.html#conditions 3 | * https://docs.python.org/3/library/asyncio-sync.html#asyncio.Condition 4 | 5 | ### events 6 | * https://pymotw.com/3/asyncio/synchronization.html#events 7 | * https://docs.python.org/3/library/asyncio-sync.html#asyncio.Event 8 | 9 | ### locks 10 | * https://pymotw.com/3/asyncio/synchronization.html#locks 11 | * file:///media/goutom/others/tech%20docs/python-3.7.4-docs-html/library/asyncio-sync.html#asyncio.Lock -------------------------------------------------------------------------------- /testfile.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | 3 | 4 | async def sleeper_coroutine(): 5 | await asyncio.sleep(10) 6 | print('executing after 10 secs sleep in coroutine') 7 | 8 | 9 | async def counter_coroutine(): 10 | await asyncio.sleep(3) 11 | print('executing after 3 secs sleep in coroutine') 12 | 13 | 14 | if __name__ == '__main__': 15 | loop = asyncio.get_event_loop() 16 | try: 17 | loop.run_until_complete(sleeper_coroutine()) 18 | print('comeback ! sleeper_coroutine finished') 19 | finally: 20 | loop.close() 21 | 22 | -------------------------------------------------------------------------------- /thread_test.py: -------------------------------------------------------------------------------- 1 | import time 2 | import random 3 | from threading import Thread 4 | 5 | 6 | class Worker(Thread): 7 | def __init__(self, number): 8 | Thread.__init__(self) 9 | self._number = number 10 | 11 | def run(self): 12 | sleep = random.randrange(1, 10) 13 | time.sleep(sleep) 14 | print(f"Worker {self._number}, slept for {sleep} seconds") 15 | 16 | 17 | if __name__ == "__main__": 18 | 19 | for i in range(1, 6): 20 | task = Worker(i) 21 | task.start() 22 | 23 | print("Total 5 Threads are queued, let's see when they finish!") -------------------------------------------------------------------------------- /multiprocessing_test.py: -------------------------------------------------------------------------------- 1 | import time 2 | import random 3 | from multiprocessing import Process, Pool 4 | 5 | 6 | class Processor(Process): 7 | def __init__(self, number): 8 | Process.__init__(self) 9 | self._number = number 10 | 11 | def run(self): 12 | sleep = random.randrange(1, 10) 13 | time.sleep(sleep) 14 | print(f"Worker {self._number}, slept for {sleep} seconds") 15 | 16 | 17 | if __name__ == "__main__": 18 | 19 | for i in range(1, 6): 20 | t = Processor(i) 21 | t.start() 22 | 23 | print("Total 5 Processes are queued, let's see when they finish!") 24 | -------------------------------------------------------------------------------- /streams/tcp_echo_client.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import logging 3 | 4 | 5 | LOGGER_FORMAT = "%(levelname)s %(asctime)s %(message)s" 6 | logging.basicConfig(format=LOGGER_FORMAT, datefmt='[%H:%M:%S]', level=logging.INFO) 7 | logging.getLogger("asyncio").setLevel(logging.DEBUG) 8 | 9 | 10 | async def tcp_echo_client(message): 11 | reader, writer = await asyncio.open_connection('127.0.0.1', 8888) 12 | 13 | logging.info(f'Send: {message!r}') 14 | writer.write(message.encode()) 15 | 16 | data = await reader.read(100) 17 | logging.info(f'Received: {data.decode()}') 18 | 19 | logging.info('Close the connection') 20 | writer.close() 21 | 22 | asyncio.run(tcp_echo_client('Hello World!'), debug=True) -------------------------------------------------------------------------------- /streams/tcp_echo_server.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | 3 | 4 | async def handle_echo(reader, writer): 5 | data = await reader.read(100) 6 | message = data.decode() 7 | addr = writer.get_extra_info('peername') 8 | 9 | print(f"Received {message!r} from {addr!r}") 10 | 11 | # print('f"Send: {message!r}"') 12 | writer.write('welcome'.encode()) 13 | await writer.drain() 14 | 15 | print("Close the connection") 16 | writer.close() 17 | 18 | 19 | async def main(): 20 | server = await asyncio.start_server(handle_echo, '127.0.0.1', 8888) 21 | addr = server.sockets[0].getsockname() 22 | print(f'Serving on {addr}') 23 | 24 | async with server: 25 | await server.serve_forever() 26 | 27 | asyncio.run(main()) 28 | -------------------------------------------------------------------------------- /sync_web_scraping.py: -------------------------------------------------------------------------------- 1 | """Synchronously download a list of webpages and time it""" 2 | from urllib.request import Request, urlopen 3 | from time import time 4 | 5 | sites = [ 6 | "https://news.ycombinator.com/", 7 | "https://www.yahoo.com/", 8 | "https://github.com/", 9 | ] 10 | 11 | 12 | def find_size(url): 13 | req = Request(url) 14 | with urlopen(req) as response: 15 | page = response.read() 16 | return len(page) 17 | 18 | 19 | def main(): 20 | for site in sites: 21 | size = find_size(site) 22 | print("Read {:8d} chars from {}".format(size, site)) 23 | 24 | 25 | if __name__ == '__main__': 26 | start_time = time() 27 | main() 28 | print("Ran in {:6.3f} secs".format(time() - start_time)) -------------------------------------------------------------------------------- /coroutines_and_tasks/task_cancel.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | 3 | 4 | async def cancel_me(): 5 | print('cancel_me(): before sleep') 6 | 7 | try: 8 | # Wait for 1 hour 9 | await asyncio.sleep(3600) 10 | except asyncio.CancelledError: 11 | print('cancel_me(): cancel sleep') 12 | raise 13 | finally: 14 | print('cancel_me(): after sleep') 15 | 16 | 17 | async def main(): 18 | # Create a "cancel_me" Task 19 | task = asyncio.create_task(cancel_me()) 20 | 21 | # Wait for 1 second 22 | await asyncio.sleep(1) 23 | 24 | task.cancel() 25 | try: 26 | await task 27 | except asyncio.CancelledError: 28 | print("main(): cancel_me is cancelled now") 29 | 30 | asyncio.run(main()) -------------------------------------------------------------------------------- /synchronization_primitives/conditions.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | 3 | 4 | async def consumer(condition, n): 5 | async with condition: 6 | print(f'consumer {n} waiting') 7 | await condition.wait() 8 | print(f'consumer {n} done') 9 | 10 | 11 | async def trigger(condition): 12 | await asyncio.sleep(5) 13 | 14 | for i in range(1, 3): 15 | async with condition: 16 | condition.notify(n=i) 17 | 18 | await asyncio.sleep(5) 19 | print('notifying remaining') 20 | async with condition: 21 | condition.notify_all() 22 | 23 | 24 | async def main(): 25 | condition = asyncio.Condition() 26 | asyncio.create_task(trigger(condition)) 27 | consumers = [consumer(condition, i) for i in range(5)] 28 | await asyncio.wait(consumers) 29 | 30 | asyncio.run(main()) -------------------------------------------------------------------------------- /future_test.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | 3 | 4 | # Define a coroutine that takes in a future 5 | async def myCoroutine(future): 6 | # simulate some 'work' 7 | await asyncio.sleep(10) 8 | # set the result of our future object 9 | future.set_result("My Coroutine-turned-future has completed") 10 | 11 | 12 | async def main(): 13 | # define a future object 14 | future = asyncio.Future() 15 | # wait for the completion of our coroutine that we've 16 | # turned into a future object using the ensure_future() function 17 | await asyncio.ensure_future(myCoroutine(future)) 18 | # Print the result of our future 19 | print(future.result()) 20 | 21 | # Spin up a quick and simple event loop 22 | # and run until completed 23 | loop = asyncio.get_event_loop() 24 | try: 25 | loop.run_until_complete(main()) 26 | finally: 27 | loop.close() -------------------------------------------------------------------------------- /synchronization_primitives/event2.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import functools 3 | 4 | 5 | def set_event(event): 6 | print('setting event in callback') 7 | event.set() 8 | 9 | 10 | async def coro1(event): 11 | print('coro1 waiting for event') 12 | await event.wait() 13 | print('coro1 triggered') 14 | 15 | 16 | async def coro2(event): 17 | print('coro2 waiting for event') 18 | await event.wait() 19 | print('coro2 triggered') 20 | 21 | 22 | async def main(): 23 | # Create a shared event 24 | event = asyncio.Event() 25 | print('event start state: {}'.format(event.is_set())) 26 | 27 | asyncio.get_running_loop().call_later(10, functools.partial(set_event, event)) 28 | 29 | await asyncio.wait([coro1(event), coro2(event)]) 30 | print('event end state: {}'.format(event.is_set())) 31 | 32 | 33 | asyncio.run(main()) 34 | 35 | 36 | -------------------------------------------------------------------------------- /synchronization_primitives/event1.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | 3 | 4 | async def waiter(event): 5 | print('waiting for it ...') 6 | await event.wait() 7 | print('... event set happened! after 5 seconds') 8 | await asyncio.sleep(10) 9 | print('waiter finished after 10 seconds') 10 | 11 | 12 | async def main(): 13 | # Create an Event object. 14 | event = asyncio.Event() 15 | 16 | # Spawn a Task to wait until 'event' is set. 17 | waiter_task = asyncio.create_task(waiter(event)) 18 | 19 | # Sleep for 1 second and set the event. 20 | await asyncio.sleep(5) 21 | event.set() 22 | 23 | # Wait until the waiter task is finished. 24 | await waiter_task 25 | print('main task finished') 26 | 27 | asyncio.run(main()) 28 | 29 | 30 | # async def coro(event): 31 | # await event.wait() 32 | # for each in range(20000000): 33 | # pass 34 | # print('coro done') 35 | # 36 | # 37 | # async def main(): 38 | # event = asyncio.Event() 39 | # task = asyncio.create_task(coro(event)) 40 | # await asyncio.sleep(5) 41 | # event.set() 42 | # # await task 43 | # print('main done') 44 | # 45 | # asyncio.run(main()) 46 | -------------------------------------------------------------------------------- /synchronization_primitives/lock1.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import functools 3 | 4 | 5 | def unlock(lock): 6 | print('callback releasing lock') 7 | lock.release() 8 | 9 | 10 | async def coro1(lock): 11 | print('coro1 waiting for the lock') 12 | async with lock: 13 | print('coro1 acquired lock') 14 | print('coro1 released lock') 15 | 16 | 17 | async def coro2(lock): 18 | print('coro2 waiting for the lock') 19 | await lock.acquire() 20 | try: 21 | print('coro2 acquired lock') 22 | finally: 23 | print('coro2 released lock') 24 | lock.release() 25 | 26 | 27 | async def main(): 28 | # Create and acquire a shared lock. 29 | lock = asyncio.Lock() 30 | print('acquiring the lock before starting coroutines') 31 | await lock.acquire() 32 | print('lock acquired: {}'.format(lock.locked())) 33 | 34 | # Schedule a callback to unlock the lock. 35 | asyncio.get_running_loop().call_later(5, functools.partial(unlock, lock)) 36 | 37 | # Run the coroutines that want to use the lock. 38 | print('waiting for coroutines') 39 | await asyncio.wait([coro1(lock), coro2(lock)]), 40 | 41 | 42 | asyncio.run(main()) -------------------------------------------------------------------------------- /get_your_ip.py: -------------------------------------------------------------------------------- 1 | from collections import namedtuple 2 | import time 3 | import asyncio 4 | from concurrent.futures import FIRST_COMPLETED 5 | import aiohttp 6 | 7 | Service = namedtuple('Service', ('name', 'url', 'ip_attr')) 8 | 9 | SERVICES = ( 10 | Service('ipify', 'https://api.ipify.org?format=json', 'ip'), 11 | Service('ip-api', 'http://ip-api.com/json', 'query') 12 | ) 13 | 14 | 15 | async def aiohttp_get_json(url): 16 | async with aiohttp.ClientSession() as session: 17 | async with session.get(url) as response: 18 | return await response.json() 19 | 20 | 21 | async def fetch_ip(service): 22 | start = time.time() 23 | print('Fetching IP from {}'.format(service.name)) 24 | 25 | json_response = await aiohttp_get_json(service.url) 26 | ip = json_response[service.ip_attr] 27 | 28 | return '{} finished with result: {}, took: {:.2f} seconds'.format( 29 | service.name, ip, time.time() - start) 30 | 31 | 32 | async def main(): 33 | futures = [fetch_ip(service) for service in SERVICES] 34 | done, pending = await asyncio.wait(futures, return_when=FIRST_COMPLETED) 35 | print(done.pop().result()) 36 | 37 | 38 | ioloop = asyncio.get_event_loop() 39 | ioloop.run_until_complete(main()) 40 | ioloop.close() -------------------------------------------------------------------------------- /get_your_ip_3.7.py: -------------------------------------------------------------------------------- 1 | from collections import namedtuple 2 | import time 3 | import asyncio 4 | from concurrent.futures import FIRST_COMPLETED 5 | import aiohttp 6 | 7 | Service = namedtuple('Service', ('name', 'url', 'ip_attr')) 8 | 9 | SERVICES = ( 10 | Service('ipify', 'https://api.ipify.org?format=json', 'ip'), 11 | Service('ip-api', 'http://ip-api.com/json', 'query') 12 | ) 13 | 14 | 15 | async def aiohttp_get_json(url): 16 | async with aiohttp.ClientSession() as session: 17 | async with session.get(url) as response: 18 | return await response.json() 19 | 20 | 21 | async def fetch_ip(service): 22 | start = time.time() 23 | print('Fetching IP from {}'.format(service.name)) 24 | 25 | json_response = await aiohttp_get_json(service.url) 26 | ip = json_response[service.ip_attr] 27 | 28 | return '{} finished with result: {}, took: {:.2f} seconds'.format( 29 | service.name, ip, time.time() - start) 30 | 31 | 32 | async def main(): 33 | tasks = set(asyncio.create_task(fetch_ip(service)) for service in SERVICES) 34 | done, pending = await asyncio.wait(tasks, return_when=FIRST_COMPLETED) 35 | for i in done: 36 | print(type(i)) 37 | for i in pending: 38 | print(type(i)) 39 | print(done.pop().result()) 40 | 41 | 42 | asyncio.run(main()) -------------------------------------------------------------------------------- /event_loop/run_in_executor.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import concurrent.futures 3 | import time 4 | 5 | 6 | def blocking_io(): 7 | # File operations (such as logging) can block the 8 | # event loop: run them in a thread pool. 9 | with open('demo_text.txt', 'rb') as f: 10 | return f.read(100) 11 | 12 | 13 | def cpu_bound(): 14 | # CPU-bound operations will block the event loop: 15 | # in general it is preferable to run them in a 16 | # process pool. 17 | return sum(i * i for i in range(10 ** 7)) 18 | 19 | 20 | async def main(): 21 | loop = asyncio.get_running_loop() 22 | 23 | # Options: 24 | 25 | # 1. Run in the default loop's executor: 26 | result = await loop.run_in_executor(None, blocking_io) 27 | print('default thread pool', result) 28 | 29 | # 2. Run in a custom thread pool: 30 | with concurrent.futures.ThreadPoolExecutor() as pool: 31 | result = await loop.run_in_executor(pool, blocking_io) 32 | print('custom thread pool', result) 33 | await asyncio.sleep(10) 34 | # 3. Run in a custom process pool: 35 | with concurrent.futures.ProcessPoolExecutor() as pool: 36 | result = await loop.run_in_executor(pool, cpu_bound) 37 | print('custom process pool', result) 38 | 39 | print('000000000000') 40 | 41 | asyncio.run(main()) -------------------------------------------------------------------------------- /async_web_scraping.py: -------------------------------------------------------------------------------- 1 | """ 2 | Asynchronously download a list of webpages and time it 3 | 4 | Dependencies: Make sure you install aiohttp using: pip install aiohttp aiodns 5 | """ 6 | import asyncio 7 | import aiohttp 8 | from time import time 9 | 10 | # Configuring logging to show timestamps 11 | import logging 12 | logging.basicConfig(format='%(asctime)s %(message)s', datefmt='[%H:%M:%S]') 13 | log = logging.getLogger() 14 | log.setLevel(logging.INFO) 15 | 16 | sites = [ 17 | "https://news.ycombinator.com/", 18 | "https://www.yahoo.com/", 19 | "https://github.com/", 20 | ] 21 | 22 | 23 | async def find_size(session, url): 24 | log.info("START {}".format(url)) 25 | async with session.get(url) as response: 26 | log.info("RESPONSE {}".format(url)) 27 | page = await response.read() 28 | log.info("PAGE {}".format(url)) 29 | return url, len(page) 30 | 31 | 32 | async def main(): 33 | tasks = [] 34 | async with aiohttp.ClientSession() as session: 35 | for site in sites: 36 | tasks.append(find_size(session, site)) 37 | results = await asyncio.gather(*tasks) 38 | for site, size in results: 39 | print("Read {:8d} chars from {}".format(size, site)) 40 | 41 | 42 | if __name__ == '__main__': 43 | start_time = time() 44 | loop = asyncio.get_event_loop() 45 | try: 46 | loop.set_debug(True) 47 | loop.run_until_complete(main()) 48 | finally: 49 | loop.close() 50 | print(f"Ran in {(time() - start_time):.3f} secs") -------------------------------------------------------------------------------- /queues/queue1.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | 3 | 4 | async def consumer(n, q, lock): 5 | print('consumer {}: starting'.format(n)) 6 | while True: 7 | print('consumer {}: waiting for item'.format(n)) 8 | item = await q.get() 9 | print('consumer {}: has item {}'.format(n, item)) 10 | if item is None: 11 | # None is the signal to stop. 12 | q.task_done() 13 | print('NONE----------') 14 | break 15 | else: 16 | await asyncio.sleep(0.01 * item) 17 | q.task_done() 18 | print('consumer {}: ending'.format(n)) 19 | 20 | 21 | async def producer(q, num_workers): 22 | print('producer: starting') 23 | # Add some numbers to the queue to simulate jobs 24 | for i in range(num_workers * 3): 25 | await q.put(i) 26 | print('producer: added task {} to the queue'.format(i)) 27 | # Add None entries in the queue 28 | # to signal the consumers to exit 29 | print('producer: adding stop signals to the queue') 30 | for i in range(num_workers): 31 | await q.put(None) 32 | print('producer: waiting for queue to empty') 33 | await q.join() 34 | print('producer: ending') 35 | 36 | 37 | async def main(num_consumers): 38 | # Create the queue with a fixed size so the producer 39 | # will block until the consumers pull some items out. 40 | q = asyncio.Queue(maxsize=num_consumers) 41 | lock = asyncio.Lock() 42 | consumers = [consumer(i, q, lock) for i in range(num_consumers)] 43 | await asyncio.wait(consumers + [producer(q, num_consumers)]) 44 | 45 | 46 | asyncio.run(main(2)) -------------------------------------------------------------------------------- /get_your_ip_3.7_error_handling.py: -------------------------------------------------------------------------------- 1 | from collections import namedtuple 2 | import time 3 | import asyncio 4 | import aiohttp 5 | import traceback 6 | 7 | Service = namedtuple('Service', ('name', 'url', 'ip_attr')) 8 | 9 | SERVICES = ( 10 | Service('ipify', 'https://api.ipify.org?format=json', 'ip'), 11 | Service('ip-api', 'http://ip-api.com/json', 'this-is-not-an-attr'), 12 | Service('borken', 'http://no-way-this-is-going-to-work.com/json', 'ip') 13 | ) 14 | 15 | 16 | async def aiohttp_get_json(url): 17 | async with aiohttp.ClientSession() as session: 18 | async with session.get(url) as response: 19 | return await response.json() 20 | 21 | 22 | async def fetch_ip(service): 23 | start = time.time() 24 | print('Fetching IP from {}'.format(service.name)) 25 | 26 | # try: 27 | # json_response = await aiohttp_get_json(service.url) 28 | # except: 29 | # return '{} is unresponsive'.format(service.name) 30 | json_response = await aiohttp_get_json(service.url) 31 | ip = json_response[service.ip_attr] 32 | return '{} finished with result: {}, took: {:.2f} seconds'.format(service.name, ip, time.time() - start) 33 | 34 | 35 | async def main(): 36 | futures = [fetch_ip(service) for service in SERVICES] 37 | done, pending = await asyncio.wait(futures) 38 | 39 | for future in done: 40 | try: 41 | print(future.result()) 42 | except: 43 | print('----------------------------------') 44 | # print("Unexpected error: {}".format(traceback.format_exc())) 45 | print("Unexpected error: {}".format(future.exception())) 46 | 47 | asyncio.run(main()) -------------------------------------------------------------------------------- /github_public_events.py: -------------------------------------------------------------------------------- 1 | import time 2 | import urllib.request 3 | import asyncio 4 | import aiohttp 5 | 6 | URL = 'https://api.github.com/events' 7 | MAX_CLIENTS = 3 8 | 9 | 10 | def fetch_sync(pid): 11 | print('Fetch sync process {} started'.format(pid)) 12 | start = time.time() 13 | response = urllib.request.urlopen(URL) 14 | datetime = response.getheader('Date') 15 | 16 | print('Process {}: {}, took: {:.2f} seconds'.format( 17 | pid, datetime, time.time() - start)) 18 | 19 | return datetime 20 | 21 | 22 | async def aiohttp_get(url): 23 | """Nothing to see here, carry on ...""" 24 | async with aiohttp.ClientSession() as session: 25 | async with session.get(url) as response: 26 | return response 27 | 28 | 29 | async def fetch_async(pid): 30 | print('Fetch async process {} started'.format(pid)) 31 | start = time.time() 32 | response = await aiohttp_get(URL) 33 | datetime = response.headers.get('Date') 34 | 35 | print('Process {}: {}, took: {:.2f} seconds'.format( 36 | pid, datetime, time.time() - start)) 37 | 38 | response.close() 39 | return datetime 40 | 41 | 42 | def synchronous(): 43 | start = time.time() 44 | for i in range(1, MAX_CLIENTS + 1): 45 | fetch_sync(i) 46 | print("Process took: {:.2f} seconds".format(time.time() - start)) 47 | 48 | 49 | async def asynchronous(): 50 | start = time.time() 51 | tasks = [asyncio.create_task(fetch_async(i)) for i in range(1, MAX_CLIENTS + 1)] 52 | await asyncio.wait(tasks) 53 | print("Process took: {:.2f} seconds".format(time.time() - start)) 54 | 55 | 56 | print('Synchronous:') 57 | synchronous() 58 | 59 | print('Asynchronous:') 60 | asyncio.run(asynchronous()) -------------------------------------------------------------------------------- /thread_pool_executor.py: -------------------------------------------------------------------------------- 1 | """ 2 | 3 | from concurrent.futures import ThreadPoolExecutor 4 | from time import sleep 5 | 6 | 7 | def return_after_5_secs(message): 8 | sleep(5) 9 | print(1/0) 10 | return message 11 | 12 | 13 | if __name__ == "__main__": 14 | 15 | pool = ThreadPoolExecutor(3) 16 | future = pool.submit(return_after_5_secs, ("hello", "brother")) 17 | print(future.done()) 18 | sleep(5) 19 | print(future.done()) 20 | print(future.result()) 21 | 22 | """ 23 | 24 | import concurrent.futures 25 | import urllib.request 26 | from time import sleep 27 | 28 | URLS = ['http://www.foxnews.com/', 29 | 'http://www.cnn.com/', 30 | 'http://europe.wsj.com/', 31 | 'http://www.bbc.co.uk/', 32 | 'http://some-made-up-domain.com/', 33 | 'https://www.prothomalo.com/', 34 | 'http://www.banglatribune.com/', 35 | 'https://www.grameenphone.com/', 36 | 'https://www.robi.com.bd/en', 37 | 'https://www.banglalink.net/bn'] 38 | 39 | 40 | def load_url(url, timeout): 41 | with urllib.request.urlopen(url, timeout=timeout) as conn: 42 | return conn.read() 43 | 44 | 45 | if __name__ == "__main__": 46 | 47 | with concurrent.futures.ThreadPoolExecutor(max_workers=2) as executor: 48 | future_to_url = {executor.submit(load_url, url, 60): url for url in URLS} 49 | for future in concurrent.futures.as_completed(future_to_url): 50 | url = future_to_url[future] 51 | try: 52 | data = future.result() 53 | except Exception as exc: 54 | print(f'{url} generated an exception: {exc}\n') 55 | else: 56 | print(f'{url} page size is {len(data)} bytes') -------------------------------------------------------------------------------- /queues/queue2.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import random 3 | import time 4 | 5 | 6 | async def worker(name, queue): 7 | while True: 8 | # Get a "work item" out of the queue. 9 | sleep_for = await queue.get() 10 | 11 | # Sleep for the "sleep_for" seconds. 12 | await asyncio.sleep(sleep_for) 13 | 14 | # Notify the queue that the "work item" has been processed. 15 | queue.task_done() 16 | 17 | print(f'{name} has slept for {sleep_for:.2f} seconds') 18 | 19 | 20 | async def main(): 21 | # Create a queue that we will use to store our "workload". 22 | queue = asyncio.Queue() 23 | 24 | # Generate random timings and put them into the queue. 25 | total_sleep_time = 0 26 | for _ in range(20): 27 | sleep_for = random.uniform(0.05, 1.0) 28 | total_sleep_time += sleep_for 29 | queue.put_nowait(sleep_for) 30 | 31 | # Create three worker tasks to process the queue concurrently. 32 | workers = [] 33 | for i in range(3): 34 | task = asyncio.create_task(worker(f'worker-{i}', queue)) 35 | workers.append(task) 36 | 37 | # Wait until the queue is fully processed. 38 | started_at = time.monotonic() 39 | await queue.join() 40 | total_slept_for = time.monotonic() - started_at 41 | print(queue.qsize()) 42 | # Cancel our worker tasks. 43 | for each_worker in workers: 44 | each_worker.cancel() 45 | # Wait until all worker tasks are cancelled. 46 | await asyncio.gather(*workers, return_exceptions=True) 47 | 48 | print('====') 49 | print(f'3 workers slept in parallel for {total_slept_for:.2f} seconds') 50 | print(f'total expected sleep time: {total_sleep_time:.2f} seconds') 51 | 52 | 53 | asyncio.run(main()) -------------------------------------------------------------------------------- /get_your_ip_timeout.py: -------------------------------------------------------------------------------- 1 | import time 2 | import random 3 | import asyncio 4 | import aiohttp 5 | import argparse 6 | from collections import namedtuple 7 | from concurrent.futures import FIRST_COMPLETED 8 | 9 | Service = namedtuple('Service', ('name', 'url', 'ip_attr')) 10 | 11 | SERVICES = ( 12 | Service('ipify', 'https://api.ipify.org?format=json', 'ip'), 13 | Service('ip-api', 'http://ip-api.com/json', 'query'), 14 | ) 15 | 16 | DEFAULT_TIMEOUT = 5 17 | 18 | 19 | async def aiohttp_get_json(url): 20 | async with aiohttp.ClientSession() as session: 21 | async with session.get(url) as response: 22 | return await response.json() 23 | 24 | 25 | async def fetch_ip(service): 26 | start = time.time() 27 | print('Fetching IP from {}'.format(service.name)) 28 | 29 | await asyncio.sleep(random.randint(1, 3) * 0.1) 30 | try: 31 | json_response = await aiohttp_get_json(service.url) 32 | except: 33 | return '{} is unresponsive'.format(service.name) 34 | 35 | ip = json_response[service.ip_attr] 36 | 37 | print('{} finished with result: {}, took: {:.2f} seconds'.format( 38 | service.name, ip, time.time() - start)) 39 | return ip 40 | 41 | 42 | async def main(timeout): 43 | response = { 44 | "message": "Result from asynchronous.", 45 | "ip": "not available" 46 | } 47 | 48 | futures = [fetch_ip(service) for service in SERVICES] 49 | done, pending = await asyncio.wait(futures, timeout=timeout, return_when=FIRST_COMPLETED) 50 | 51 | for future in pending: 52 | future.cancel() 53 | 54 | for future in done: 55 | response["ip"] = future.result() 56 | 57 | print(response) 58 | 59 | 60 | parser = argparse.ArgumentParser() 61 | parser.add_argument( 62 | '-t', '--timeout', help='Timeout to use, defaults to {}'.format(DEFAULT_TIMEOUT), 63 | default=DEFAULT_TIMEOUT, type=float) 64 | args = parser.parse_args() 65 | 66 | print("Using a {} timeout".format(args.timeout)) 67 | # asyncio.run(main(args.timeout)) 68 | asyncio.run(main(DEFAULT_TIMEOUT)) -------------------------------------------------------------------------------- /github_public_events_futures.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 3 | Creating concurrency 4 | So far we’ve been using a single method of creating and retrieving results from coroutines, 5 | creating a set of tasks and waiting for all of them to finish. 6 | 7 | But coroutines can be scheduled to run or retrieve their results in different ways. 8 | Imagine a scenario where we need to process the results of the HTTP GET requests as soon as they arrive, t 9 | he process is actually quite similar than in our previous example: 10 | 11 | The code in this case is only slightly different, we’re gathering the coroutines into a list, 12 | each of them ready to be scheduled and executed. 13 | The as_completed function returns an iterator that will yield a completed future as they come in. 14 | Now don’t tell me that’s not cool. By the way, as_completed is originally from the concurrent.futures module. 15 | ''' 16 | 17 | import datetime 18 | import time 19 | import random 20 | import asyncio 21 | import aiohttp 22 | 23 | URL = 'https://api.github.com/events' 24 | MAX_CLIENTS = 3 25 | 26 | 27 | async def aiohttp_get(url): 28 | async with aiohttp.ClientSession() as session: 29 | async with session.get(url) as response: 30 | return response 31 | 32 | 33 | async def fetch_async(pid): 34 | start = time.time() 35 | sleepy_time = random.randint(10, 15) 36 | print('Fetch async process {} started, sleeping for {} seconds'.format(pid, sleepy_time)) 37 | 38 | await asyncio.sleep(sleepy_time) 39 | 40 | response = await aiohttp_get(URL) 41 | datetime = response.headers.get('Date') 42 | 43 | response.close() 44 | return 'Process {}: {}, took: {:.2f} seconds'.format(pid, datetime, time.time() - start) 45 | 46 | 47 | async def main(): 48 | start = time.time() 49 | futures = [fetch_async(i) for i in range(1, MAX_CLIENTS + 1)] 50 | for i, future in enumerate(asyncio.as_completed(futures)): 51 | result = await future 52 | print('{} {}'.format(">>" * (i + 1), result)) 53 | 54 | print("Process took: {:.2f} seconds".format(time.time() - start)) 55 | 56 | 57 | asyncio.run(main()) -------------------------------------------------------------------------------- /process_pool_executor.py: -------------------------------------------------------------------------------- 1 | """ 2 | 3 | import concurrent.futures 4 | import math 5 | from time import sleep 6 | 7 | PRIMES = [ 8 | 112272535095293, 9 | 112582705942171, 10 | 112272535095293, 11 | 115280095190773, 12 | 115797848077099, 13 | 1099726899285419] 14 | 15 | 16 | def is_prime(n): 17 | if n == 112272535095293: 18 | sleep(8) 19 | if n % 2 == 0: 20 | return False 21 | 22 | sqrt_n = int(math.floor(math.sqrt(n))) 23 | for i in range(3, sqrt_n + 1, 2): 24 | if n % i == 0: 25 | return False 26 | return True 27 | 28 | 29 | def main(): 30 | with concurrent.futures.ProcessPoolExecutor() as executor: 31 | for number, prime in zip(PRIMES, executor.map(is_prime, PRIMES)): 32 | print('%d is prime: %s' % (number, prime)) 33 | 34 | 35 | if __name__ == '__main__': 36 | main() 37 | 38 | 39 | """ 40 | 41 | import concurrent.futures 42 | import urllib.request 43 | from time import sleep 44 | 45 | URLS = ['http://www.foxnews.com/', 46 | 'http://www.cnn.com/', 47 | 'http://europe.wsj.com/', 48 | 'http://www.bbc.co.uk/', 49 | 'http://some-made-up-domain.com/', 50 | 'https://www.prothomalo.com/', 51 | 'http://www.banglatribune.com/', 52 | 'https://www.grameenphone.com/', 53 | 'https://www.robi.com.bd/en', 54 | 'https://www.banglalink.net/bn'] 55 | 56 | 57 | def load_url(url, timeout): 58 | with urllib.request.urlopen(url, timeout=timeout) as conn: 59 | return conn.read() 60 | 61 | 62 | if __name__ == "__main__": 63 | 64 | with concurrent.futures.ProcessPoolExecutor(max_workers=3) as executor: 65 | future_to_url = {executor.submit(load_url, url, 60): url for url in URLS} 66 | for future in concurrent.futures.as_completed(future_to_url): 67 | url = future_to_url[future] 68 | try: 69 | data = future.result() 70 | except Exception as exc: 71 | print(f'{url} generated an exception: {exc}\n') 72 | else: 73 | print(f'{url} page size is {len(data)} bytes') -------------------------------------------------------------------------------- /generators_and_coroutines.py: -------------------------------------------------------------------------------- 1 | # example 1 2 | 3 | 4 | # def simple_gen(): 5 | # yield "Hello" 6 | # yield "World" 7 | # 8 | # 9 | # gen = simple_gen() 10 | # print(type(gen)) 11 | # print(next(gen)) 12 | # print(next(gen)) 13 | # for each in gen: 14 | # print(each) 15 | 16 | 17 | # example 2 18 | 19 | # def coro(): 20 | # hello = yield "Hello" 21 | # yield hello 22 | # 23 | # 24 | # c = coro() 25 | # print(next(c)) 26 | # print(c.send("World")) 27 | 28 | # example 3 29 | 30 | # def generate_nums(): 31 | # num = 0 32 | # while True: 33 | # lis = [] 34 | # for i in range(1, 11): 35 | # t = num + i 36 | # lis.append(t) 37 | # yield lis 38 | # num = t 39 | # 40 | # 41 | # nums = generate_nums() 42 | # 43 | # for i, x in enumerate(nums): 44 | # print(x) 45 | # if i == 9: 46 | # break 47 | 48 | # example 4 49 | 50 | # import asyncio 51 | # import datetime 52 | # import random 53 | # 54 | # 55 | # @asyncio.coroutine 56 | # def display_date(num, loop): 57 | # end_time = loop.time() + 50.0 58 | # idx = 1 59 | # while True: 60 | # print(f"{idx} Loop: {num} Time: {datetime.datetime.now()}") 61 | # if (loop.time()) >= end_time: 62 | # break 63 | # idx += 1 64 | # yield from asyncio.sleep(random.randint(0, 5)) 65 | # 66 | # 67 | # loop = asyncio.get_event_loop() 68 | # asyncio.ensure_future(display_date(1, loop)) 69 | # asyncio.ensure_future(display_date(2, loop)) 70 | # loop.run_forever() 71 | 72 | # example 5 73 | # import asyncio 74 | # import datetime 75 | # import random 76 | # 77 | # 78 | # async def display_date(num, loop): 79 | # end_time = loop.time() + 50.0 80 | # while True: 81 | # print("Loop: {} Time: {}".format(num, datetime.datetime.now())) 82 | # if (loop.time() + 1.0) >= end_time: 83 | # break 84 | # await asyncio.sleep(random.randint(1, 5)) 85 | # 86 | # 87 | # loop = asyncio.get_event_loop() 88 | # d1 = display_date(1, loop) 89 | # d2 = display_date(2, loop) 90 | # print(type(d1)) 91 | # asyncio.ensure_future(d1) 92 | # asyncio.ensure_future(d2) 93 | # loop.run_forever() 94 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # asyncio 2 | * [asyncio](https://docs.python.org/3/library/asyncio.html) introduced in python 3.4. 3 | And the native coroutines and async/await syntax came in Python 3.5. Python 3.7 has also few upgrades. So I recommend you to use Python 3.7. 4 | Celery is fantastic for running concurrent tasks out of process, but there are certain times you would need to run multiple tasks in a single thread inside a single process. 5 | 6 | * One more important thing to mention is that all the code is running in a single thread. 7 | So if you expect that one part of the program will be executed in the background while your program will be doing something else, this won’t happen. 8 | All asynchronous code is running in a single thread. 9 | 10 | ### Tutorials 11 | 12 | * https://yeray.dev/python/asyncio/asyncio-for-the-working-python-developer 13 | * https://arunrocks.com/get-started-with-async-and-await/ 14 | * http://masnun.com/2015/11/20/python-asyncio-future-task-and-the-event-loop.html 15 | * http://masnun.rocks/2016/10/06/async-python-the-different-forms-of-concurrency/ 16 | * http://masnun.com/2015/11/13/python-generators-coroutines-native-coroutines-and-async-await.html 17 | * https://www.aeracode.org/2018/02/19/python-async-simplified/ 18 | * https://www.aeracode.org/2018/06/04/django-async-roadmap/ 19 | * https://hackernoon.com/asyncio-for-the-working-python-developer-5c468e6e2e8e 20 | * https://medium.com/python-pandemonium/asyncio-coroutine-patterns-beyond-await-a6121486656f 21 | * https://medium.com/@yeraydiazdiaz/asyncio-coroutine-patterns-errors-and-cancellation-3bb422e961ff 22 | * https://edmundmartin.com/writing-a-web-crawler-in-python-3-5-using-asyncio/ 23 | 24 | ### event loop 25 | An event loop is a loop that can register tasks to be executed, execute them, delay or even cancel them and handle different events related to these operations. 26 | 27 | Generally, we schedule multiple async functions to the event loop. The loop runs one function, while that function waits for IO, it pauses it and runs another. 28 | 29 | When the first function completes IO, it is resumed. Thus two or more functions can co-operatively run together. This the main goal of an event loop. 30 | Event loop is single threaded. 31 | 32 | ### coroutine 33 | * A coroutine is like a special function which can suspend and resume execution. 34 | They work like lightweight threads. 35 | * Don’t perform slow or blocking operations synchronously inside coroutines. 36 | * Don’t call a coroutine directly like a regular function call. Either schedule it in an event loop or await it from another coroutine. 37 | 38 | * A very common blocking task is, of course, fetching data from an HTTP service. 39 | Use aiohttp library for non-blocking HTTP requests retrieving data from Github’s public event API and simply take the Date response header. 40 | Native coroutines use the async and await keywords, as follows: 41 | 42 | ```python 43 | 44 | import asyncio 45 | 46 | async def sleeper_coroutine(): 47 | await asyncio.sleep(3) 48 | print('executing after 3 secs sleep in coroutine') 49 | 50 | if __name__ == '__main__': 51 | loop = asyncio.get_event_loop() 52 | loop.run_until_complete(sleeper_coroutine()) 53 | print('comeback ! coroutine finished') 54 | 55 | ``` 56 | 57 | We can define coroutines in 2 distinct ways : 58 | ```python 59 | 60 | import asyncio 61 | 62 | async def m_func1(): 63 | print("Coroutine 1") 64 | 65 | @asyncio.coroutine 66 | def m_func2() 67 | print("Coroutine 2") 68 | 69 | ``` 70 | 71 | * **async** commonly used before a function definition as async def, 72 | it indicates that you are defining a (native) coroutine. 73 | * **await** keyword which must be only used inside a coroutine. 74 | 75 | ### Futures / Tasks 76 | A Future is an object that is supposed to have a result in the future. 77 | A Task is a subclass of Future that wraps a coroutine. When the coroutine finishes, the result of the Task is realized. 78 | 79 | -------------------------------------------------------------------------------- /hacker_news_comment_calc.py: -------------------------------------------------------------------------------- 1 | """ 2 | A recursive function solves a problem by simplifying the input until 3 | we arrive at a base trivial case and then combining the results up the stack. 4 | Assume we want to calculate the number of comments of a particular post in 5 | Hacker News by recursively aggregating the number of descendents. 6 | """ 7 | 8 | import asyncio 9 | import argparse 10 | import logging 11 | from urllib.parse import urlparse, parse_qs 12 | from datetime import datetime 13 | 14 | import aiohttp 15 | import async_timeout 16 | 17 | 18 | LOGGER_FORMAT = '%(asctime)s %(message)s' 19 | URL_TEMPLATE = "https://hacker-news.firebaseio.com/v0/item/{}.json" 20 | FETCH_TIMEOUT = 10 21 | 22 | parser = argparse.ArgumentParser( 23 | description='Calculate the comments of a Hacker News post.') 24 | parser.add_argument('--id', type=int, default=8863, 25 | help='ID of the post in HN, defaults to 8863') 26 | parser.add_argument('--url', type=str, help='URL of a post in HN') 27 | parser.add_argument('--verbose', action='store_true', help='Detailed output') 28 | 29 | 30 | logging.basicConfig(format=LOGGER_FORMAT, datefmt='[%H:%M:%S]', level=logging.INFO) 31 | 32 | fetch_counter = 0 33 | 34 | 35 | async def fetch(session, url): 36 | """Fetch a URL using aiohttp returning parsed JSON response. 37 | As suggested by the aiohttp docs we reuse the session. 38 | """ 39 | global fetch_counter 40 | with async_timeout.timeout(FETCH_TIMEOUT): 41 | fetch_counter += 1 42 | async with session.get(url) as response: 43 | return await response.json() 44 | 45 | 46 | async def post_number_of_comments(loop, session, post_id): 47 | """Retrieve data for current post and recursively for all comments. 48 | """ 49 | url = URL_TEMPLATE.format(post_id) 50 | now = datetime.now() 51 | response = await fetch(session, url) 52 | log.debug('{:^6} > Fetching of {} took {} seconds'.format( 53 | post_id, url, (datetime.now() - now).total_seconds())) 54 | 55 | if 'kids' not in response: # base case, there are no comments 56 | return 0 57 | 58 | # calculate this post's comments as number of comments 59 | number_of_comments = len(response['kids']) 60 | 61 | # create recursive tasks for all comments 62 | log.debug('{:^6} > Fetching {} child posts'.format( 63 | post_id, number_of_comments)) 64 | tasks = [post_number_of_comments( 65 | loop, session, kid_id) for kid_id in response['kids']] 66 | 67 | # schedule the tasks and retrieve results 68 | results = await asyncio.gather(*tasks) 69 | 70 | # reduce the descendents comments and add it to this post's 71 | number_of_comments += sum(results) 72 | log.debug('{:^6} > {} comments'.format(post_id, number_of_comments)) 73 | 74 | return number_of_comments 75 | 76 | 77 | def id_from_HN_url(url): 78 | """Returns the value of the `id` query arg of a URL if present, or None. 79 | """ 80 | parse_result = urlparse(url) 81 | try: 82 | return parse_qs(parse_result.query)['id'][0] 83 | except (KeyError, IndexError): 84 | return None 85 | 86 | 87 | async def main(loop, post_id): 88 | """Async entry point coroutine. 89 | """ 90 | now = datetime.now() 91 | async with aiohttp.ClientSession(loop=loop) as session: 92 | now = datetime.now() 93 | comments = await post_number_of_comments(loop, session, post_id) 94 | log.info( 95 | '> Calculating comments took {:.2f} seconds and {} fetches'.format( 96 | (datetime.now() - now).total_seconds(), fetch_counter)) 97 | 98 | return comments 99 | 100 | 101 | if __name__ == '__main__': 102 | args = parser.parse_args() 103 | if args.verbose: 104 | log.setLevel(logging.DEBUG) 105 | 106 | post_id = id_from_HN_url(args.url) if args.url else args.id 107 | 108 | loop = asyncio.get_event_loop() 109 | comments = loop.run_until_complete(main(loop, post_id)) 110 | log.info("-- Post {} has {} comments".format(post_id, comments)) 111 | 112 | loop.close() -------------------------------------------------------------------------------- /event_loop/demo_text.txt: -------------------------------------------------------------------------------- 1 | Event loop provides mechanisms to schedule callback functions to be called at some point in the future. Event loop uses monotonic clocks to track time. 2 | 3 | loop.call_later(delay, callback, *args, context=None) 4 | Schedule callback to be called after the given delay number of seconds (can be either an int or a float). 5 | 6 | An instance of asyncio.TimerHandle is returned which can be used to cancel the callback. 7 | 8 | callback will be called exactly once. If two callbacks are scheduled for exactly the same time, the order in which they are called is undefined. 9 | 10 | The optional positional args will be passed to the callback when it is called. If you want the callback to be called with keyword arguments use functools.partial(). 11 | 12 | An optional keyword-only context argument allows specifying a custom contextvars.Context for the callback to run in. The current context is used when no context is provided. 13 | 14 | Changed in version 3.7: The context keyword-only parameter was added. See PEP 567 for more details. 15 | 16 | Changed in version 3.7.1: In Python 3.7.0 and earlier with the default event loop implementation, the delay could not exceed one day. This has been fixed in Python 3.7.1. 17 | 18 | loop.call_at(when, callback, *args, context=None) 19 | Schedule callback to be called at the given absolute timestamp when (an int or a float), using the same time reference as loop.time(). 20 | 21 | This method’s behavior is the same as call_later(). 22 | 23 | An instance of asyncio.TimerHandle is returned which can be used to cancel the callback. 24 | 25 | Changed in version 3.7: The context keyword-only parameter was added. See PEP 567 for more details. 26 | 27 | Changed in version 3.7.1: In Python 3.7.0 and earlier with the default event loop implementation, the difference between when and the current time could not exceed one day. This has been fixed in Python 3.7.1. 28 | 29 | loop.time() 30 | Return the current time, as a float value, according to the event loop’s internal monotonic clock. 31 | 32 | 33 | 34 | Event loop provides mechanisms to schedule callback functions to be called at some point in the future. Event loop uses monotonic clocks to track time. 35 | 36 | loop.call_later(delay, callback, *args, context=None) 37 | Schedule callback to be called after the given delay number of seconds (can be either an int or a float). 38 | 39 | An instance of asyncio.TimerHandle is returned which can be used to cancel the callback. 40 | 41 | callback will be called exactly once. If two callbacks are scheduled for exactly the same time, the order in which they are called is undefined. 42 | 43 | The optional positional args will be passed to the callback when it is called. If you want the callback to be called with keyword arguments use functools.partial(). 44 | 45 | An optional keyword-only context argument allows specifying a custom contextvars.Context for the callback to run in. The current context is used when no context is provided. 46 | 47 | Changed in version 3.7: The context keyword-only parameter was added. See PEP 567 for more details. 48 | 49 | Changed in version 3.7.1: In Python 3.7.0 and earlier with the default event loop implementation, the delay could not exceed one day. This has been fixed in Python 3.7.1. 50 | 51 | loop.call_at(when, callback, *args, context=None) 52 | Schedule callback to be called at the given absolute timestamp when (an int or a float), using the same time reference as loop.time(). 53 | 54 | This method’s behavior is the same as call_later(). 55 | 56 | An instance of asyncio.TimerHandle is returned which can be used to cancel the callback. 57 | 58 | Changed in version 3.7: The context keyword-only parameter was added. See PEP 567 for more details. 59 | 60 | Changed in version 3.7.1: In Python 3.7.0 and earlier with the default event loop implementation, the difference between when and the current time could not exceed one day. This has been fixed in Python 3.7.1. 61 | 62 | loop.time() 63 | Return the current time, as a float value, according to the event loop’s internal monotonic clock. 64 | Event loop provides mechanisms to schedule callback functions to be called at some point in the future. Event loop uses monotonic clocks to track time. 65 | 66 | loop.call_later(delay, callback, *args, context=None) 67 | Schedule callback to be called after the given delay number of seconds (can be either an int or a float). 68 | 69 | An instance of asyncio.TimerHandle is returned which can be used to cancel the callback. 70 | 71 | callback will be called exactly once. If two callbacks are scheduled for exactly the same time, the order in which they are called is undefined. 72 | 73 | The optional positional args will be passed to the callback when it is called. If you want the callback to be called with keyword arguments use functools.partial(). 74 | 75 | An optional keyword-only context argument allows specifying a custom contextvars.Context for the callback to run in. The current context is used when no context is provided. 76 | 77 | Changed in version 3.7: The context keyword-only parameter was added. See PEP 567 for more details. 78 | 79 | Changed in version 3.7.1: In Python 3.7.0 and earlier with the default event loop implementation, the delay could not exceed one day. This has been fixed in Python 3.7.1. 80 | 81 | loop.call_at(when, callback, *args, context=None) 82 | Schedule callback to be called at the given absolute timestamp when (an int or a float), using the same time reference as loop.time(). 83 | 84 | This method’s behavior is the same as call_later(). 85 | 86 | An instance of asyncio.TimerHandle is returned which can be used to cancel the callback. 87 | 88 | Changed in version 3.7: The context keyword-only parameter was added. See PEP 567 for more details. 89 | 90 | Changed in version 3.7.1: In Python 3.7.0 and earlier with the default event loop implementation, the difference between when and the current time could not exceed one day. This has been fixed in Python 3.7.1. 91 | 92 | loop.time() 93 | Return the current time, as a float value, according to the event loop’s internal monotonic clock. 94 | Event loop provides mechanisms to schedule callback functions to be called at some point in the future. Event loop uses monotonic clocks to track time. 95 | 96 | loop.call_later(delay, callback, *args, context=None) 97 | Schedule callback to be called after the given delay number of seconds (can be either an int or a float). 98 | 99 | An instance of asyncio.TimerHandle is returned which can be used to cancel the callback. 100 | 101 | callback will be called exactly once. If two callbacks are scheduled for exactly the same time, the order in which they are called is undefined. 102 | 103 | The optional positional args will be passed to the callback when it is called. If you want the callback to be called with keyword arguments use functools.partial(). 104 | 105 | An optional keyword-only context argument allows specifying a custom contextvars.Context for the callback to run in. The current context is used when no context is provided. 106 | 107 | Changed in version 3.7: The context keyword-only parameter was added. See PEP 567 for more details. 108 | 109 | Changed in version 3.7.1: In Python 3.7.0 and earlier with the default event loop implementation, the delay could not exceed one day. This has been fixed in Python 3.7.1. 110 | 111 | loop.call_at(when, callback, *args, context=None) 112 | Schedule callback to be called at the given absolute timestamp when (an int or a float), using the same time reference as loop.time(). 113 | 114 | This method’s behavior is the same as call_later(). 115 | 116 | An instance of asyncio.TimerHandle is returned which can be used to cancel the callback. 117 | 118 | Changed in version 3.7: The context keyword-only parameter was added. See PEP 567 for more details. 119 | 120 | Changed in version 3.7.1: In Python 3.7.0 and earlier with the default event loop implementation, the difference between when and the current time could not exceed one day. This has been fixed in Python 3.7.1. 121 | 122 | loop.time() 123 | Return the current time, as a float value, according to the event loop’s internal monotonic clock. 124 | Event loop provides mechanisms to schedule callback functions to be called at some point in the future. Event loop uses monotonic clocks to track time. 125 | 126 | loop.call_later(delay, callback, *args, context=None) 127 | Schedule callback to be called after the given delay number of seconds (can be either an int or a float). 128 | 129 | An instance of asyncio.TimerHandle is returned which can be used to cancel the callback. 130 | 131 | callback will be called exactly once. If two callbacks are scheduled for exactly the same time, the order in which they are called is undefined. 132 | 133 | The optional positional args will be passed to the callback when it is called. If you want the callback to be called with keyword arguments use functools.partial(). 134 | 135 | An optional keyword-only context argument allows specifying a custom contextvars.Context for the callback to run in. The current context is used when no context is provided. 136 | 137 | Changed in version 3.7: The context keyword-only parameter was added. See PEP 567 for more details. 138 | 139 | Changed in version 3.7.1: In Python 3.7.0 and earlier with the default event loop implementation, the delay could not exceed one day. This has been fixed in Python 3.7.1. 140 | 141 | loop.call_at(when, callback, *args, context=None) 142 | Schedule callback to be called at the given absolute timestamp when (an int or a float), using the same time reference as loop.time(). 143 | 144 | This method’s behavior is the same as call_later(). 145 | 146 | An instance of asyncio.TimerHandle is returned which can be used to cancel the callback. 147 | 148 | Changed in version 3.7: The context keyword-only parameter was added. See PEP 567 for more details. 149 | 150 | Changed in version 3.7.1: In Python 3.7.0 and earlier with the default event loop implementation, the difference between when and the current time could not exceed one day. This has been fixed in Python 3.7.1. 151 | 152 | loop.time() 153 | Return the current time, as a float value, according to the event loop’s internal monotonic clock. 154 | Event loop provides mechanisms to schedule callback functions to be called at some point in the future. Event loop uses monotonic clocks to track time. 155 | 156 | loop.call_later(delay, callback, *args, context=None) 157 | Schedule callback to be called after the given delay number of seconds (can be either an int or a float). 158 | 159 | An instance of asyncio.TimerHandle is returned which can be used to cancel the callback. 160 | 161 | callback will be called exactly once. If two callbacks are scheduled for exactly the same time, the order in which they are called is undefined. 162 | 163 | The optional positional args will be passed to the callback when it is called. If you want the callback to be called with keyword arguments use functools.partial(). 164 | 165 | An optional keyword-only context argument allows specifying a custom contextvars.Context for the callback to run in. The current context is used when no context is provided. 166 | 167 | Changed in version 3.7: The context keyword-only parameter was added. See PEP 567 for more details. 168 | 169 | Changed in version 3.7.1: In Python 3.7.0 and earlier with the default event loop implementation, the delay could not exceed one day. This has been fixed in Python 3.7.1. 170 | 171 | loop.call_at(when, callback, *args, context=None) 172 | Schedule callback to be called at the given absolute timestamp when (an int or a float), using the same time reference as loop.time(). 173 | 174 | This method’s behavior is the same as call_later(). 175 | 176 | An instance of asyncio.TimerHandle is returned which can be used to cancel the callback. 177 | 178 | Changed in version 3.7: The context keyword-only parameter was added. See PEP 567 for more details. 179 | 180 | Changed in version 3.7.1: In Python 3.7.0 and earlier with the default event loop implementation, the difference between when and the current time could not exceed one day. This has been fixed in Python 3.7.1. 181 | 182 | loop.time() 183 | Return the current time, as a float value, according to the event loop’s internal monotonic clock. 184 | Event loop provides mechanisms to schedule callback functions to be called at some point in the future. Event loop uses monotonic clocks to track time. 185 | 186 | loop.call_later(delay, callback, *args, context=None) 187 | Schedule callback to be called after the given delay number of seconds (can be either an int or a float). 188 | 189 | An instance of asyncio.TimerHandle is returned which can be used to cancel the callback. 190 | 191 | callback will be called exactly once. If two callbacks are scheduled for exactly the same time, the order in which they are called is undefined. 192 | 193 | The optional positional args will be passed to the callback when it is called. If you want the callback to be called with keyword arguments use functools.partial(). 194 | 195 | An optional keyword-only context argument allows specifying a custom contextvars.Context for the callback to run in. The current context is used when no context is provided. 196 | 197 | Changed in version 3.7: The context keyword-only parameter was added. See PEP 567 for more details. 198 | 199 | Changed in version 3.7.1: In Python 3.7.0 and earlier with the default event loop implementation, the delay could not exceed one day. This has been fixed in Python 3.7.1. 200 | 201 | loop.call_at(when, callback, *args, context=None) 202 | Schedule callback to be called at the given absolute timestamp when (an int or a float), using the same time reference as loop.time(). 203 | 204 | This method’s behavior is the same as call_later(). 205 | 206 | An instance of asyncio.TimerHandle is returned which can be used to cancel the callback. 207 | 208 | Changed in version 3.7: The context keyword-only parameter was added. See PEP 567 for more details. 209 | 210 | Changed in version 3.7.1: In Python 3.7.0 and earlier with the default event loop implementation, the difference between when and the current time could not exceed one day. This has been fixed in Python 3.7.1. 211 | 212 | loop.time() 213 | Return the current time, as a float value, according to the event loop’s internal monotonic clock. 214 | Event loop provides mechanisms to schedule callback functions to be called at some point in the future. Event loop uses monotonic clocks to track time. 215 | 216 | loop.call_later(delay, callback, *args, context=None) 217 | Schedule callback to be called after the given delay number of seconds (can be either an int or a float). 218 | 219 | An instance of asyncio.TimerHandle is returned which can be used to cancel the callback. 220 | 221 | callback will be called exactly once. If two callbacks are scheduled for exactly the same time, the order in which they are called is undefined. 222 | 223 | The optional positional args will be passed to the callback when it is called. If you want the callback to be called with keyword arguments use functools.partial(). 224 | 225 | An optional keyword-only context argument allows specifying a custom contextvars.Context for the callback to run in. The current context is used when no context is provided. 226 | 227 | Changed in version 3.7: The context keyword-only parameter was added. See PEP 567 for more details. 228 | 229 | Changed in version 3.7.1: In Python 3.7.0 and earlier with the default event loop implementation, the delay could not exceed one day. This has been fixed in Python 3.7.1. 230 | 231 | loop.call_at(when, callback, *args, context=None) 232 | Schedule callback to be called at the given absolute timestamp when (an int or a float), using the same time reference as loop.time(). 233 | 234 | This method’s behavior is the same as call_later(). 235 | 236 | An instance of asyncio.TimerHandle is returned which can be used to cancel the callback. 237 | 238 | Changed in version 3.7: The context keyword-only parameter was added. See PEP 567 for more details. 239 | 240 | Changed in version 3.7.1: In Python 3.7.0 and earlier with the default event loop implementation, the difference between when and the current time could not exceed one day. This has been fixed in Python 3.7.1. 241 | 242 | loop.time() 243 | Return the current time, as a float value, according to the event loop’s internal monotonic clock.Event loop provides mechanisms to schedule callback functions to be called at some point in the future. Event loop uses monotonic clocks to track time. 244 | 245 | loop.call_later(delay, callback, *args, context=None) 246 | Schedule callback to be called after the given delay number of seconds (can be either an int or a float). 247 | 248 | An instance of asyncio.TimerHandle is returned which can be used to cancel the callback. 249 | 250 | callback will be called exactly once. If two callbacks are scheduled for exactly the same time, the order in which they are called is undefined. 251 | 252 | The optional positional args will be passed to the callback when it is called. If you want the callback to be called with keyword arguments use functools.partial(). 253 | 254 | An optional keyword-only context argument allows specifying a custom contextvars.Context for the callback to run in. The current context is used when no context is provided. 255 | 256 | Changed in version 3.7: The context keyword-only parameter was added. See PEP 567 for more details. 257 | 258 | Changed in version 3.7.1: In Python 3.7.0 and earlier with the default event loop implementation, the delay could not exceed one day. This has been fixed in Python 3.7.1. 259 | 260 | loop.call_at(when, callback, *args, context=None) 261 | Schedule callback to be called at the given absolute timestamp when (an int or a float), using the same time reference as loop.time(). 262 | 263 | This method’s behavior is the same as call_later(). 264 | 265 | An instance of asyncio.TimerHandle is returned which can be used to cancel the callback. 266 | 267 | Changed in version 3.7: The context keyword-only parameter was added. See PEP 567 for more details. 268 | 269 | Changed in version 3.7.1: In Python 3.7.0 and earlier with the default event loop implementation, the difference between when and the current time could not exceed one day. This has been fixed in Python 3.7.1. 270 | 271 | loop.time() 272 | Return the current time, as a float value, according to the event loop’s internal monotonic clock. 273 | --------------------------------------------------------------------------------