Skip to content

Commit 0d254fe

Browse files
committed
Refactor entire repo.
1 parent b670c3b commit 0d254fe

32 files changed

Lines changed: 825 additions & 686 deletions
-189 KB
Binary file not shown.

.github/aiohttp@2x.jpg

375 KB
Loading

README.md

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Python Asyncio Tutorial
1+
# Asynchronous HTTP Requests Tutorial
22

33
![Python](https://img.shields.io/badge/Python-v^3.9-blue.svg?logo=python&longCache=true&logoColor=white&colorB=5e81ac&style=flat-square&colorA=4c566a)
44
![Asyncio](https://img.shields.io/badge/Asyncio-v^3.4.3-blue.svg?longCache=true&logo=python&style=flat-square&logoColor=white&colorB=5e81ac&colorA=4c566a)
@@ -9,22 +9,11 @@
99
[![GitHub Stars](https://img.shields.io/github/stars/hackersandslackers/asyncio-tutorial.svg?style=flat-square&colorA=4c566a&logo=GitHub&colorB=ebcb8b)](https://github.com/hackersandslackers/flask-blueprint-tutorial/stargazers)
1010
[![GitHub Forks](https://img.shields.io/github/forks/hackersandslackers/asyncio-tutorial.svg?style=flat-square&colorA=4c566a&logo=GitHub&colorB=ebcb8b)](https://github.com/hackersandslackers/flask-blueprint-tutorial/network)
1111

12-
![Asyncio](https://raw.githubusercontent.com/hackersandslackers/asyncio-tutorial/master/.github/96C20F51-485C-4B54-9FEB-4C0CA5C4ABEC.jpeg)
13-
14-
**Source code demonstrating asynchronous Python for the corresponding Hackersandslackers posts.**
15-
16-
### Part I
17-
18-
Introduction to asynchronous Python with Asyncio.
19-
20-
https://hackersandslackers.com/python-concurrency-asyncio/
21-
22-
### Part II
12+
![Asyncio](.github/aiohttp@2x.jpg)
2313

2414
Make asynchronous HTTP requests and write to disk using [**asyncio**](https://docs.python.org/3/library/asyncio.html), [**aiohttp**](https://docs.aiohttp.org/en/stable/), & [**aiofiles**](https://github.com/Tinche/aiofiles).
2515

26-
https://hackersandslackers.com/async-requests-with-aiohttp/
27-
16+
Source code demonstrating asynchronous Python for the corresponding Hackersandslackers post: https://hackersandslackers.com/async-requests-aiohttp-aiofiles/
2817

2918
## Getting Started
3019

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
"""Make hundreds of requests concurrently and save responses to disk."""
2+
from asyncio import Task
3+
from typing import Tuple, List, Optional
4+
5+
import asyncio
6+
import time
7+
from time import perf_counter as timer
8+
9+
import aiofiles
10+
from aiofiles.threadpool.text import AsyncTextIOWrapper
11+
from aiohttp import ClientSession
12+
from config import FETCHED_URL_TITLES, HTML_HEADERS
13+
from logger import LOGGER
14+
15+
from .data import urls
16+
from .loops import inspect_event_loop
17+
from .tasks import create_tasks
18+
19+
20+
async def init_script():
21+
"""Initiate script by preparing an output file prior to executing tasks."""
22+
start_time = timer()
23+
async with aiofiles.open(FETCHED_URL_TITLES, mode="w+") as output_file:
24+
await output_file.write("title,url\n")
25+
done, pending = await create_and_execute_tasks(output_file)
26+
if len(pending) == 0:
27+
await output_file.close()
28+
29+
LOGGER.success(f"Executed {__name__} in {time.perf_counter() - start_time:0.2f} seconds.")
30+
31+
32+
async def create_and_execute_tasks(output_file: AsyncTextIOWrapper) -> Tuple[List[Optional[Task]], List[Optional[Task]]]:
33+
"""
34+
Open async HTTP session & execute created tasks.
35+
36+
:param AsyncTextIOWrapper output_file: Filepath to local .json file to write output to.
37+
38+
:returns: Tuple[List[Optional[Task]], List[Optional[Task]]]
39+
"""
40+
async with ClientSession(headers=HTML_HEADERS) as session:
41+
task_list = await create_tasks(session, urls, output_file)
42+
inspect_event_loop()
43+
done, pending = await asyncio.wait(task_list)
44+
return done, pending
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
"""Parse data from local files."""
2+
from config import CSV_FILEPATH
3+
4+
from .parser import parse_urls_from_csv
5+
6+
urls = parse_urls_from_csv(CSV_FILEPATH)
Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@
33
from typing import List
44

55

6-
def parse_urls(filepath: str) -> List[str]:
6+
def parse_urls_from_csv(filepath: str) -> List[str]:
77
"""
88
Parse a single-column CSV into a Python list of URLs.
99
10+
:param str filepath: Local path to CSV containing URLs to be parsed.
11+
1012
:returns: List[str]
1113
"""
1214
urls = []

aiohttp_aiofiles_tutorial/data/tests/__init__.py

Whitespace-only changes.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
from typing import List
2+
3+
import pytest
4+
from aiohttp_aiofiles_tutorial.data.parser import parse_urls_from_csv
5+
from config import CSV_FILEPATH
6+
7+
8+
def test_parse_urls():
9+
10+
urls = parse_urls_from_csv(CSV_FILEPATH)
11+
12+
assert (type(urls)) == list
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
"""Fetch URLs, extract their contents, and write output to file."""
2+
from aiofiles.threadpool.text import AsyncTextIOWrapper
3+
from aiohttp import ClientError, ClientSession, InvalidURL
4+
from logger import LOGGER
5+
6+
from .writer import write_to_output_file
7+
8+
9+
async def fetch_url_and_save_title(
10+
session: ClientSession, url: str, output_file: AsyncTextIOWrapper, total_count: int, i: int
11+
):
12+
"""
13+
Fetch raw HTML from a URL prior to extracting output.
14+
15+
:param ClientSession session: Async HTTP requests session.
16+
:param str url: Target URL to be fetched.
17+
:param AsyncTextIOWrapper output_file: Filepath to local .json file to write output to.
18+
:param int total_count: Total number of URLs to be fetched.
19+
:param int i: Current iteration of URL out of total URLs.
20+
"""
21+
try:
22+
async with session.get(url) as resp:
23+
html_body = await resp.text()
24+
await write_to_output_file(html_body, url, output_file, total_count, i)
25+
except InvalidURL as e:
26+
LOGGER.error(f"Unable to fetch invalid URL `{url}`: {e}")
27+
except ClientError as e:
28+
LOGGER.error(f"ClientError while fetching URL `{url}`: {e}")
29+
except Exception as e:
30+
LOGGER.error(f"Unexpected error while fetching URL `{url}`: {e}")

0 commit comments

Comments
 (0)