Skip to content

Commit 0ed0cae

Browse files
committed
Add taskgroup code (and requirements)
1 parent 2a85d7c commit 0ed0cae

3 files changed

Lines changed: 108 additions & 0 deletions

File tree

code/ch03-concurrency/groups.py

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