Skip to content

Commit 4503d45

Browse files
committed
PyDelhi 2017 content
1 parent e973ef6 commit 4503d45

File tree

366 files changed

+9094
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

366 files changed

+9094
-0
lines changed

pydelhi2017concurrency/LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2017 Anand B Pillai
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

pydelhi2017concurrency/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# pydelhi2017concurrency
2+
Concurency workshop in PyDelhi 2017
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
""" Fetch a URL using async IO """
2+
import asyncio
3+
import aiohttp
4+
import io
5+
6+
async def fetch_page(url):
7+
""" Asynchronous URL fetcher """
8+
9+
future = aiohttp.request('GET', url)
10+
# Wait for the future
11+
response = await future
12+
13+
return response
14+
15+
loop = asyncio.get_event_loop()
16+
urls = ('http://www.google.com',
17+
'http://www.yahoo.com',
18+
'http://www.facebook.com',
19+
'http://www.reddit.com',
20+
'http://www.twitter.com')
21+
22+
tasks = map(fetch_page, urls)
23+
# Wait for tasks
24+
done, pending = loop.run_until_complete(asyncio.wait(tasks, timeout=120))
25+
loop.close()
26+
27+
for future in done:
28+
response = future.result()
29+
print(response)
30+
response.close()
31+
32+
33+
34+
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
""" Fetch a URL and process its response using async io """
2+
3+
import asyncio
4+
import aiohttp
5+
import io
6+
7+
async def fetch_page(url):
8+
""" Asynchronous URL fetcher """
9+
10+
future = aiohttp.request('GET', url)
11+
# Wait for the future
12+
response = await future
13+
14+
return response
15+
16+
async def parse_response(done):
17+
""" Parse responses of fetch """
18+
19+
for future in done:
20+
response = future.result()
21+
data = await response.read()
22+
print('Response for URL',response.url,'=>', response.status, len(data))
23+
response.close()
24+
25+
loop = asyncio.get_event_loop()
26+
urls = ('http://www.google.com',
27+
'http://www.yahoo.com',
28+
'http://www.facebook.com',
29+
'http://www.reddit.com',
30+
'http://www.twitter.com')
31+
32+
33+
tasks = map(fetch_page, urls)
34+
# Wait for futures
35+
done, pending = loop.run_until_complete(asyncio.wait(tasks, timeout=120))
36+
# Wait for response processing
37+
loop.run_until_complete(parse_response(done))
38+
loop.close()
39+
40+
41+
42+
43+
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
""" Co-operative multitasking example ported to asyncio """
2+
3+
import asyncio
4+
import time
5+
6+
def number_generator(m, n):
7+
""" A number generator co-routine in range(m...n+1) """
8+
yield from range(m, n+1)
9+
10+
# @asyncio.coroutine
11+
# async
12+
async def prime_filter(m, n):
13+
""" Prime number co-routine """
14+
15+
primes = []
16+
for i in number_generator(m, n):
17+
if i % 2 == 0: continue
18+
flag = True
19+
20+
for j in range(3, int(i**0.5+1), 2):
21+
if i % j == 0:
22+
flag = False
23+
break
24+
25+
if flag:
26+
print('Prime=>',i)
27+
primes.append(i)
28+
29+
# Uncomment this out and comment next line and see what happens !!!
30+
await asyncio.sleep(1.0)
31+
32+
# time.sleep(1)
33+
34+
return tuple(primes)
35+
36+
async def square_mapper(m, n):
37+
""" Square mapper co-routine """
38+
39+
squares = []
40+
for i in number_generator(m, n):
41+
print('Square=>',i*i)
42+
squares.append(i*i)
43+
# Uncomment this out and comment next line and see what happens !!!
44+
await asyncio.sleep(1.0)
45+
# time.sleep(1)
46+
47+
return squares
48+
49+
def print_result(future):
50+
print('Result=>',future.result())
51+
52+
loop = asyncio.get_event_loop()
53+
future = asyncio.gather(prime_filter(10, 50), square_mapper(10, 50))
54+
future.add_done_callback(print_result)
55+
loop.run_until_complete(future)
56+
57+
loop.close()
58+
59+
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
""" Simple web crawler using concurrent futures """
2+
3+
from concurrent.futures import ProcessPoolExecutor
4+
from queue import Queue
5+
import requests
6+
import sys
7+
import urllib
8+
from bs4 import BeautifulSoup as bs
9+
10+
def fetch_url(url):
11+
response = requests.get(url)
12+
return (response.url, response.status_code, response.content)
13+
14+
15+
if __name__ == "__main__":
16+
base_url = sys.argv[1]
17+
18+
with ProcessPoolExecutor(max_workers=5) as executor:
19+
urls = [base_url]
20+
url_dict = {}
21+
url_dict[base_url] = 1
22+
23+
while len(urls):
24+
results = executor.map(fetch_url, urls, timeout=120)
25+
urls = []
26+
27+
for result in results:
28+
url, status, data = result
29+
if status == 200:
30+
# Save and parse the text
31+
print('Fetched',url)
32+
soup = bs(data, "lxml")
33+
# Fetch all child links
34+
child_links = filter(None, [item.get('href', '') for item in soup.findAll('a')])
35+
# Make full links
36+
child_urls = [urllib.parse.urljoin(base_url, curl) for curl in child_links if curl[0] != '#']
37+
# Push this to queue
38+
for url in child_urls:
39+
print('Pushing',url)
40+
if not url in url_dict:
41+
url_dict[url] = 1
42+
urls.append(url)
43+
44+
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
from concurrent.futures import ProcessPoolExecutor
2+
import requests
3+
4+
urls = ('http://www.google.com',
5+
'http://www.yahoo.com',
6+
'http://www.facebook.com',
7+
'http://www.reddit.com',
8+
'http://www.twitter.com')
9+
10+
def fetch_url(url):
11+
response = requests.get(url)
12+
return (response.url, response.status_code, response.text)
13+
14+
15+
with ProcessPoolExecutor(max_workers=5) as executor:
16+
results = executor.map(fetch_url, urls, timeout=120)
17+
print(results)
18+
for result in results:
19+
url, status, data = result
20+
print('Response for URL',url,'=>', status, len(data))
21+
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
from concurrent.futures import ProcessPoolExecutor, as_completed
2+
3+
def is_prime(n):
4+
""" Check for input number primality """
5+
6+
for i in range(3, int(n**0.5+1), 2):
7+
if n % i == 0:
8+
print(n,'is not prime')
9+
return False
10+
11+
print(n,'is prime')
12+
return True
13+
14+
if __name__ == "__main__":
15+
numbers = [1297337, 1116281, 104395303, 472882027, 533000389, 817504243, 982451653, 112272535095293, 115280095190773, 1099726899285419]*100
16+
17+
with ProcessPoolExecutor(max_workers=4) as executor:
18+
future_map = {executor.submit(is_prime, number): number for number in numbers}
19+
for future in as_completed(future_map):
20+
number = future_map[future]
21+
status = future.result()
22+
if status:
23+
print('Number',number,'is prime')
24+
else:
25+
print('Number',number,'aint prime')
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
"""
2+
3+
Convert images in folder to thumbnails using concurrent futures
4+
5+
"""
6+
7+
import os
8+
import sys
9+
from PIL import Image
10+
from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor, as_completed
11+
import mimetypes
12+
13+
def thumbnail_image(filename, size=(64,64), format='.png'):
14+
""" Convert image thumbnails, given a filename """
15+
16+
try:
17+
im=Image.open(filename)
18+
im.thumbnail(size, Image.ANTIALIAS)
19+
20+
basename = os.path.basename(filename)
21+
thumb_filename = os.path.join('thumbs',
22+
basename.rsplit('.')[0] + '_thumb.png')
23+
im.save(thumb_filename)
24+
print('Saved',thumb_filename)
25+
return True
26+
27+
except Exception as e:
28+
print('Error converting file',filename)
29+
print(e)
30+
return False
31+
32+
def directory_walker(start_dir):
33+
""" Walk a directory and generate list of valid images """
34+
35+
for root,dirs,files in os.walk(os.path.expanduser(start_dir)):
36+
for f in files:
37+
filename = os.path.join(root,f)
38+
# Only process if its a type of image
39+
file_type = mimetypes.guess_type(filename.lower())[0]
40+
if file_type != None and file_type.startswith('image/'):
41+
yield filename
42+
43+
if __name__ == '__main__':
44+
root_dir = os.path.expanduser('~/Pictures/')
45+
if '--process' in sys.argv:
46+
executor = ProcessPoolExecutor(max_workers=10)
47+
else:
48+
executor = ThreadPoolExecutor(max_workers=10)
49+
50+
with executor:
51+
future_map = {executor.submit(thumbnail_image, filename): filename for filename in directory_walker(root_dir)}
52+
for future in as_completed(future_map):
53+
num = future_map[future]
54+
status = future.result()
55+
if status:
56+
print('Thumbnail of',future_map[future],'saved')
57+
58+
59+
60+

0 commit comments

Comments
 (0)