Skip to content

Commit cd80bf0

Browse files
committed
Task Group demo code.
1 parent 869734f commit cd80bf0

File tree

3 files changed

+110
-0
lines changed

3 files changed

+110
-0
lines changed

code/ch03-concurrency/groups.py

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import asyncio
2+
from asyncio import TaskGroup
3+
from pprint import pprint
4+
5+
import httpx
6+
from typing import List, Optional
7+
8+
import pydantic
9+
from pydantic import BaseModel
10+
11+
12+
class SearchItem(BaseModel):
13+
category: str
14+
id: Optional[int]
15+
item_id: Optional[int]
16+
url: str
17+
title: str
18+
description: str
19+
20+
21+
class SearchResponse(BaseModel):
22+
elapsed_ms: float
23+
keywords: List[str]
24+
results: List[SearchItem] = pydantic.Field(default_factory=list)
25+
episodes: List[SearchItem] = pydantic.Field(default_factory=list)
26+
27+
28+
def cleanup(resp: SearchResponse):
29+
# Unfortunately, Talk Python's and Python Bytes' APIs vary slightly.
30+
# This method will unify them.
31+
resp.episodes = resp.episodes or resp.results
32+
resp.results = resp.episodes or resp.results
33+
34+
for r in resp.results:
35+
r.id = r.id or r.item_id
36+
37+
38+
async def search_podcast(text: str, server: str) -> list[str]:
39+
search_url = f'https://{server}/api/search?q={text}'
40+
async with httpx.AsyncClient() as client:
41+
resp = await client.get(search_url)
42+
resp.raise_for_status()
43+
44+
data = resp.json()
45+
resp = SearchResponse(**data)
46+
cleanup(resp)
47+
48+
return [f'{r.id}: {r.title}' for r in resp.results if r.category.lower() == 'episode']
49+
50+
51+
async def search():
52+
# pydantic beanie mongodb odm <-- is a good search string
53+
text = input("What keyword to you want to look for? ").strip().lower()
54+
55+
async with TaskGroup() as tg:
56+
tp = tg.create_task(search_podcast(text, 'search.talkpython.fm'))
57+
pb = tg.create_task(search_podcast(text, 'search.pythonbytes.fm'))
58+
59+
# results = await asyncio.gather(tp, pb)
60+
# pprint(results)
61+
# return
62+
63+
print("Done with search.")
64+
print()
65+
print("Results from Talk Python:")
66+
for title in tp.result():
67+
print(f'* {title}')
68+
print()
69+
print("Results from Python Bytes:")
70+
for title in pb.result():
71+
print(f'* {title}')
72+
73+
74+
if __name__ == '__main__':
75+
asyncio.run(search())

requirements.in

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
httpx
2+
pydantic

requirements.txt

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
#
2+
# This file is autogenerated by pip-compile with Python 3.11
3+
# by the following command:
4+
#
5+
# pip-compile requirements.in
6+
#
7+
anyio==3.6.2
8+
# via httpcore
9+
certifi==2022.12.7
10+
# via
11+
# httpcore
12+
# httpx
13+
h11==0.14.0
14+
# via httpcore
15+
httpcore==0.16.3
16+
# via httpx
17+
httpx==0.23.1
18+
# via -r requirements.in
19+
idna==3.4
20+
# via
21+
# anyio
22+
# rfc3986
23+
pydantic==1.10.2
24+
# via -r requirements.in
25+
rfc3986[idna2008]==1.5.0
26+
# via httpx
27+
sniffio==1.3.0
28+
# via
29+
# anyio
30+
# httpcore
31+
# httpx
32+
typing-extensions==4.4.0
33+
# via pydantic

0 commit comments

Comments
 (0)