├── .DS_Store
├── .github
└── workflows
│ └── main.yml
├── .gitignore
├── .gitpod.Dockerfile
├── .gitpod.yml
├── CH_01_04_begin.py
├── CH_01_04_end.py
├── CH_01_05.py
├── CH_01_06.py
├── CH_02_01_begin.py
├── CH_02_01_end.py
├── CH_02_02_client.py
├── CH_02_02_server.py
├── CH_02_03
├── create_json.py
└── show_repos.html
├── CH_02_04
├── create_json.py
├── repo_data.json
└── show_repos.html
├── CH_03_02.py
├── CH_03_03.py
├── CH_03_04.py
├── CH_04_03.py
├── CH_04_05.py
├── CH_04_06.py
├── NOTICE.md
├── README.md
├── dump.rdb
├── repo_data.json
├── requirements.txt
├── show_repos.html
└── templates
├── base.html
├── chat.html
└── chat_redis.html
/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LinkedInLearning/async-python-foundations-applied-concepts-2422322/753e508de6929528e1c13b7ac785820547742a06/.DS_Store
--------------------------------------------------------------------------------
/.github/workflows/main.yml:
--------------------------------------------------------------------------------
1 | name: Copy To Branches
2 | on:
3 | workflow_dispatch:
4 | jobs:
5 | copy-to-branches:
6 | runs-on: ubuntu-latest
7 | steps:
8 | - uses: actions/checkout@v2
9 | with:
10 | fetch-depth: 0
11 | - name: Copy To Branches Action
12 | uses: planetoftheweb/copy-to-branches@v1
13 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 |
6 | # C extensions
7 | *.so
8 |
9 | # Distribution / packaging
10 | .Python
11 | build/
12 | develop-eggs/
13 | dist/
14 | downloads/
15 | eggs/
16 | .eggs/
17 | lib/
18 | lib64/
19 | parts/
20 | sdist/
21 | var/
22 | wheels/
23 | share/python-wheels/
24 | *.egg-info/
25 | .installed.cfg
26 | *.egg
27 | MANIFEST
28 |
29 | # PyInstaller
30 | # Usually these files are written by a python script from a template
31 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
32 | *.manifest
33 | *.spec
34 |
35 | # Installer logs
36 | pip-log.txt
37 | pip-delete-this-directory.txt
38 |
39 | # Unit test / coverage reports
40 | htmlcov/
41 | .tox/
42 | .nox/
43 | .coverage
44 | .coverage.*
45 | .cache
46 | nosetests.xml
47 | coverage.xml
48 | *.cover
49 | *.py,cover
50 | .hypothesis/
51 | .pytest_cache/
52 | cover/
53 |
54 | # Translations
55 | *.mo
56 | *.pot
57 |
58 | # Django stuff:
59 | *.log
60 | local_settings.py
61 | db.sqlite3
62 | db.sqlite3-journal
63 |
64 | # Flask stuff:
65 | instance/
66 | .webassets-cache
67 |
68 | # Scrapy stuff:
69 | .scrapy
70 |
71 | # Sphinx documentation
72 | docs/_build/
73 |
74 | # PyBuilder
75 | .pybuilder/
76 | target/
77 |
78 | # Jupyter Notebook
79 | .ipynb_checkpoints
80 |
81 | # IPython
82 | profile_default/
83 | ipython_config.py
84 |
85 | # pyenv
86 | # For a library or package, you might want to ignore these files since the code is
87 | # intended to run in multiple environments; otherwise, check them in:
88 | # .python-version
89 |
90 | # pipenv
91 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
92 | # However, in case of collaboration, if having platform-specific dependencies or dependencies
93 | # having no cross-platform support, pipenv may install dependencies that don't work, or not
94 | # install all needed dependencies.
95 | #Pipfile.lock
96 |
97 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow
98 | __pypackages__/
99 |
100 | # Celery stuff
101 | celerybeat-schedule
102 | celerybeat.pid
103 |
104 | # SageMath parsed files
105 | *.sage.py
106 |
107 | # Environments
108 | .env
109 | .venv
110 | env/
111 | venv/
112 | ENV/
113 | env.bak/
114 | venv.bak/
115 |
116 | # Spyder project settings
117 | .spyderproject
118 | .spyproject
119 |
120 | # Rope project settings
121 | .ropeproject
122 |
123 | # mkdocs documentation
124 | /site
125 |
126 | # mypy
127 | .mypy_cache/
128 | .dmypy.json
129 | dmypy.json
130 |
131 | # Pyre type checker
132 | .pyre/
133 |
134 | # pytype static type analyzer
135 | .pytype/
136 |
137 | # Cython debug symbols
138 | cython_debug/
139 |
140 | .vscode/
141 |
--------------------------------------------------------------------------------
/.gitpod.Dockerfile:
--------------------------------------------------------------------------------
1 | FROM gitpod/workspace-full
2 | RUN sudo apt-get update && sudo apt-get install -y redis-server && sudo rm -rf /var/lib/apt/lists/*
3 |
4 |
--------------------------------------------------------------------------------
/.gitpod.yml:
--------------------------------------------------------------------------------
1 |
2 | image:
3 | file: .gitpod.Dockerfile
4 |
5 | tasks:
6 | - init: >
7 | python3 -m pip install --upgrade pip &&
8 | pip3 install -r requirements.txt
9 |
--------------------------------------------------------------------------------
/CH_01_04_begin.py:
--------------------------------------------------------------------------------
1 | import time
2 | from datetime import datetime
3 |
4 | import click
5 |
6 |
7 | def sleep_and_print(seconds):
8 | print(f"starting {seconds} sleep 😴")
9 | time.sleep(seconds)
10 | print(f"finished {seconds} sleep ⏰")
11 | return seconds
12 |
13 |
14 | start = datetime.now()
15 | print([sleep_and_print(3), sleep_and_print(6)])
16 | click.secho(f"{datetime.now()-start}", bold=True, bg="blue", fg="white")
17 |
--------------------------------------------------------------------------------
/CH_01_04_end.py:
--------------------------------------------------------------------------------
1 | import asyncio
2 | from datetime import datetime
3 | import click
4 |
5 |
6 | async def sleep_and_print(seconds):
7 | print(f"starting async {seconds} sleep 😴")
8 | await asyncio.sleep(seconds)
9 | print(f"finished async {seconds} sleep ⏰")
10 | return seconds
11 |
12 |
13 | async def main():
14 | # using arguments
15 | results = await asyncio.gather(sleep_and_print(3), sleep_and_print(6))
16 |
17 | # building list
18 | # coroutines_list = []
19 | # for i in range(1, 11):
20 | # coroutines_list.append(sleep_and_print(i))
21 | # results = await asyncio.gather(*coroutines_list)
22 | print(results)
23 |
24 |
25 | start = datetime.now()
26 | asyncio.run(main())
27 | click.secho(f"{datetime.now()-start}", bold=True, bg="blue", fg="white")
28 |
--------------------------------------------------------------------------------
/CH_01_05.py:
--------------------------------------------------------------------------------
1 | import asyncio
2 | from datetime import datetime
3 | import click
4 |
5 |
6 | async def sleep_five():
7 | pass # your code here
8 |
9 |
10 | async def sleep_three_then_five():
11 | pass # your code here
12 |
13 |
14 | async def main():
15 | results = "your code"
16 | print(results)
17 |
18 |
19 | start = datetime.now()
20 | asyncio.run(main())
21 | click.secho(f"{datetime.now()-start}", bold=True, bg="blue", fg="white")
22 |
--------------------------------------------------------------------------------
/CH_01_06.py:
--------------------------------------------------------------------------------
1 | import asyncio
2 | from datetime import datetime
3 | import click
4 |
5 |
6 | async def sleep_five():
7 | await asyncio.sleep(5)
8 | print("sleep five done")
9 |
10 |
11 | async def sleep_three_then_five():
12 | await asyncio.sleep(3)
13 | await sleep_five()
14 |
15 |
16 | async def main():
17 | await asyncio.gather(sleep_five(), sleep_three_then_five())
18 |
19 |
20 |
21 | start = datetime.now()
22 | asyncio.run(main())
23 | click.secho(f"{datetime.now()-start}", bold=True, bg="blue", fg="white")
24 |
--------------------------------------------------------------------------------
/CH_02_01_begin.py:
--------------------------------------------------------------------------------
1 | from datetime import datetime
2 | from pprint import pprint
3 |
4 | import click
5 | import requests
6 |
7 | urls = [
8 | "http://httpbin.org/get?text=python",
9 | "http://httpbin.org/get?text=is",
10 | "http://httpbin.org/get?text=fun",
11 | "http://httpbin.org/get?text=and",
12 | "http://httpbin.org/get?text=useful",
13 | "http://httpbin.org/get?text=you",
14 | "http://httpbin.org/get?text=can",
15 | "http://httpbin.org/get?text=almost",
16 | "http://httpbin.org/get?text=do",
17 | "http://httpbin.org/get?text=anything",
18 | "http://httpbin.org/get?text=with",
19 | "http://httpbin.org/get?text=it",
20 | ] # 12 requests
21 |
22 |
23 | def get_args(url):
24 | return requests.get(url).json()["args"]
25 |
26 |
27 | start = datetime.now()
28 | pprint([get_args(url) for url in urls])
29 | click.secho(f"{datetime.now()-start}", bold=True, bg="blue", fg="white")
30 |
--------------------------------------------------------------------------------
/CH_02_01_end.py:
--------------------------------------------------------------------------------
1 | from datetime import datetime
2 | from pprint import pprint
3 | import asyncio
4 |
5 | import aiohttp
6 | import click
7 |
8 | urls = [
9 | "http://httpbin.org/get?text=python",
10 | "http://httpbin.org/get?text=is",
11 | "http://httpbin.org/get?text=fun",
12 | "http://httpbin.org/get?text=and",
13 | "http://httpbin.org/get?text=useful",
14 | "http://httpbin.org/get?text=you",
15 | "http://httpbin.org/get?text=can",
16 | "http://httpbin.org/get?text=almost",
17 | "http://httpbin.org/get?text=do",
18 | "http://httpbin.org/get?text=anything",
19 | "http://httpbin.org/get?text=with",
20 | "http://httpbin.org/get?text=it",
21 | ]
22 |
23 |
24 | async def fetch_args(session, url):
25 | async with session.get(url) as response:
26 | data = await response.json()
27 | return data["args"]
28 |
29 |
30 | async def main():
31 | async with aiohttp.ClientSession() as session:
32 | # create a collection of coroutines(can be done with comprehension )
33 | fetch_coroutines = []
34 | for url in urls:
35 | fetch_coroutines.append(fetch_args(session, url))
36 | # waik up coroutines with gather
37 | data = await asyncio.gather(*fetch_coroutines)
38 | pprint(data)
39 |
40 |
41 | start = datetime.now()
42 | asyncio.run(main())
43 | click.secho(f"{datetime.now()-start}", bold=True, bg="blue", fg="white")
44 |
--------------------------------------------------------------------------------
/CH_02_02_client.py:
--------------------------------------------------------------------------------
1 | import asyncio
2 | import click
3 |
4 | import websockets
5 |
6 |
7 | def render_response(message):
8 | click.clear()
9 | if "BLASTOFF" in message:
10 | click.secho(message, blink=True, bold=True, fg="red")
11 | else:
12 | click.secho(message, bold=True, fg="blue")
13 |
14 |
15 | async def astronout():
16 | uri = "ws://localhost:8765"
17 | async with websockets.connect(uri) as websocket:
18 | is_ready = input("Are you ready? ")
19 |
20 | await websocket.send(is_ready)
21 | async for message in websocket:
22 | render_response(message)
23 | if "BLASTOFF" in message:
24 | return
25 |
26 |
27 | async def main():
28 | await asyncio.gather(astronout(), astronout(), astronout(), astronout())
29 |
30 |
31 | asyncio.run(main())
32 |
--------------------------------------------------------------------------------
/CH_02_02_server.py:
--------------------------------------------------------------------------------
1 | import asyncio
2 | import websockets
3 | import click
4 | from contextlib import suppress
5 |
6 |
7 | async def blastoff(websocket):
8 | click.secho(">> begin blastoff")
9 | for i in range(25):
10 | await websocket.send(f"\n\n\n>> {' ' * i}🚀")
11 | await asyncio.sleep(0.03)
12 | for i in range(3):
13 | await asyncio.sleep(0.5)
14 | await websocket.send(f"\n\n\n>> 🚀🚀🚀🚀🚀🚀🚀🚀 BLASTOFF 🚀🚀🚀🚀🚀🚀🚀🚀 <<")
15 |
16 |
17 | async def huston(websocket, path):
18 | click.clear()
19 | async for message in websocket:
20 | if "yes" in message.lower():
21 | click.secho(">> begin countdown")
22 | for i in reversed(range(1, 11)):
23 | await websocket.send(f"\n\n\n>> Taking off in: {i}")
24 | await asyncio.sleep(0.8)
25 | with suppress(Exception):
26 | await blastoff(websocket)
27 |
28 |
29 | PORT = 8765
30 | click.secho(f"--- listening for websocket connections on port: {PORT} ---")
31 | start_server = websockets.serve(huston, "localhost", PORT)
32 |
33 | asyncio.get_event_loop().run_until_complete(start_server)
34 | asyncio.get_event_loop().run_forever()
35 |
--------------------------------------------------------------------------------
/CH_02_03/create_json.py:
--------------------------------------------------------------------------------
1 | from pprint import pprint
2 | import asyncio
3 | import json
4 |
5 | import aiohttp
6 |
7 | URIS = (
8 | "https://api.github.com/orgs/python",
9 | "https://api.github.com/orgs/django",
10 | "https://api.github.com/orgs/pallets",
11 | )
12 |
13 |
14 | def write_to_file(data):
15 | with open("repo_data.json", "w") as jf:
16 | json.dump(data, jf)
17 |
18 |
19 | async def fetch(session, url):
20 | async with session.get(url) as response:
21 | data = await response.json()
22 | return {"name": data["name"], "avatar_url": data["avatar_url"]}
23 |
24 |
25 | async def main():
26 | async with aiohttp.ClientSession() as session:
27 | """
28 | TODO
29 | 1. use 'await asyncio.gather' and 'fetch' to get repo names and avatar_urls
30 | 2. use 'write_to_file' to create a json file with the results
31 | 3. text your code
32 | a. 'cd CH_02_03'
33 | b. 'python -m http.server'
34 | NOTE: Feel free to use CH_02_01_end.py for reference.
35 | """
36 | data = "this is a place holder"
37 |
38 | asyncio.run(main())
39 |
--------------------------------------------------------------------------------
/CH_02_03/show_repos.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Document
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
40 |
41 |
--------------------------------------------------------------------------------
/CH_02_04/create_json.py:
--------------------------------------------------------------------------------
1 | from pprint import pprint
2 | import asyncio
3 | import json
4 |
5 | import aiohttp
6 |
7 | URIS = (
8 | "https://api.github.com/orgs/python",
9 | "https://api.github.com/orgs/django",
10 | "https://api.github.com/orgs/pallets",
11 | )
12 |
13 |
14 | def write_to_file(data):
15 | with open("repo_data.json", "w") as jf:
16 | json.dump(data, jf)
17 |
18 |
19 | async def fetch(session, url):
20 | async with session.get(url) as response:
21 | data = await response.json()
22 | return {"name": data["name"], "avatar_url": data["avatar_url"]}
23 |
24 |
25 | async def main():
26 | async with aiohttp.ClientSession() as session:
27 | """
28 | TODO
29 | 1. use 'await asyncio.gather' and 'fetch' to get repo names and avatar_urls
30 | 2. use 'write_to_file' to create a json file with the results
31 | 3. text your code
32 | a. 'cd CH_02_03'
33 | b. 'python -m http.server'
34 | NOTE: Feel free to use CH_02_01_end.py for reference.
35 | """
36 | data = await asyncio.gather(*[fetch(session, uri) for uri in URIS])
37 | write_to_file(data)
38 |
39 |
40 | loop = asyncio.get_event_loop()
41 | loop.run_until_complete(main())
42 |
--------------------------------------------------------------------------------
/CH_02_04/repo_data.json:
--------------------------------------------------------------------------------
1 | [{"name": "Python", "avatar_url": "https://avatars.githubusercontent.com/u/1525981?v=4"}, {"name": "Django", "avatar_url": "https://avatars.githubusercontent.com/u/27804?v=4"}, {"name": "Pallets", "avatar_url": "https://avatars.githubusercontent.com/u/16748505?v=4"}]
--------------------------------------------------------------------------------
/CH_02_04/show_repos.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Document
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
39 |
40 |
--------------------------------------------------------------------------------
/CH_03_02.py:
--------------------------------------------------------------------------------
1 | import asyncio
2 | import aioredis
3 | import click
4 | import json
5 |
6 |
7 | class Chat:
8 | def __init__(self, room_name):
9 | self.room_name = room_name
10 |
11 | async def start_db(self):
12 | self.redis = await aioredis.create_redis_pool("redis://localhost")
13 | await self.redis.set("room_name", self.room_name)
14 |
15 | async def save_message(self, message_dictionary):
16 | room_name = await self.redis.get("room_name")
17 | message_json = json.dumps(message_dictionary)
18 | await self.redis.rpush(room_name, message_json)
19 |
20 | async def clear_db(self):
21 | await self.redis.flushall()
22 |
23 | async def get_all_messages(self):
24 | room_name = await self.redis.get("room_name")
25 | message_jsons = await self.redis.lrange(room_name, 0, -1, encoding="utf-8")
26 | messages = []
27 | for message in message_jsons:
28 | message_dictionary = json.loads(message)
29 | messages.append(message_dictionary)
30 | return messages
31 |
32 |
33 | async def main():
34 | chat_db = Chat("messages")
35 | await chat_db.start_db()
36 | await chat_db.save_message({"handle": "first_user", "message": "hey"})
37 | await chat_db.save_message({"handle": "first_user", "message": "hey"})
38 | await chat_db.save_message({"handle": "second_user", "message": "What's up?"})
39 | await chat_db.save_message({"handle": "first_user", "message": "all good!"})
40 |
41 | chat_messages = await chat_db.get_all_messages()
42 |
43 | click.secho(f" Chat ", fg="cyan", bold=True, bg="yellow")
44 | for message in chat_messages:
45 | click.secho(f' {message["handle"]} | {message["message"]} ', fg="cyan")
46 | await chat_db.clear_db()
47 |
48 | asyncio.run(main())
49 |
--------------------------------------------------------------------------------
/CH_03_03.py:
--------------------------------------------------------------------------------
1 | import json
2 | import asyncio
3 |
4 | import click
5 | import aioredis
6 |
7 | class Chat:
8 | def __init__(self, room_name):
9 | self.room_name = room_name
10 |
11 | async def start_db(self):
12 | self.redis = await aioredis.create_redis_pool("redis://localhost")
13 | await self.redis.set("room_name", self.room_name)
14 |
15 | async def save_message(self, message_dictionary):
16 | room_name = await self.redis.get("room_name")
17 | message_json = json.dumps(message_dictionary)
18 | await self.redis.rpush(room_name, message_json)
19 |
20 | async def clear_db(self):
21 | await self.redis.flushall()
22 |
23 | async def get_all_messages(self):
24 | room_name = await self.redis.get("room_name")
25 | message_jsons = await self.redis.lrange(room_name, 0, -1, encoding="utf-8")
26 | messages = []
27 | for message in message_jsons:
28 | message_dictionary = json.loads(message)
29 | messages.append(message_dictionary)
30 | return messages
31 |
32 | async def get_name(self):
33 | return # your code here make sure you use encoding="utf-8"
34 |
35 |
36 | async def main():
37 | chat_db = Chat("messages")
38 | await chat_db.start_db()
39 | await chat_db.save_message({"handle": "first_user", "message": "hey"})
40 | await chat_db.save_message({"handle": "first_user", "message": "hey"})
41 | await chat_db.save_message({"handle": "second_user", "message": "What's up?"})
42 | await chat_db.save_message({"handle": "first_user", "message": "all good!"})
43 |
44 | chat_messages = await chat_db.get_all_messages()
45 |
46 | click.secho(f" Chat ", fg="cyan", bold=True, bg="yellow")
47 | for message in chat_messages:
48 | click.secho(f' {message["handle"]} | {message["message"]} ', fg="cyan")
49 | await chat_db.clear_db()
50 |
51 | asyncio.run(main())
--------------------------------------------------------------------------------
/CH_03_04.py:
--------------------------------------------------------------------------------
1 | import json
2 | import asyncio
3 |
4 | import click
5 | import aioredis
6 |
7 | class Chat:
8 | def __init__(self, room_name):
9 | self.room_name = room_name
10 |
11 | async def start_db(self):
12 | self.redis = await aioredis.create_redis_pool("redis://localhost")
13 | await self.redis.set("room_name", self.room_name)
14 |
15 | async def save_message(self, message_dictionary):
16 | room_name = await self.redis.get("room_name")
17 | message_json = json.dumps(message_dictionary)
18 | await self.redis.rpush(room_name, message_json)
19 |
20 | async def clear_db(self):
21 | await self.redis.flushall()
22 |
23 | async def get_all_messages(self):
24 | room_name = await self.redis.get("room_name")
25 | message_jsons = await self.redis.lrange(room_name, 0, -1, encoding="utf-8")
26 | messages = []
27 | for message in message_jsons:
28 | message_dictionary = json.loads(message)
29 | messages.append(message_dictionary)
30 | return messages
31 |
32 | async def get_name(self):
33 | return await self.redis.get("room_name", encoding="utf-8")
34 |
35 |
36 | async def main():
37 | chat_db = Chat("messages")
38 | await chat_db.start_db()
39 | await chat_db.save_message({"handle": "first_user", "message": "hey"})
40 | await chat_db.save_message({"handle": "first_user", "message": "hey"})
41 | await chat_db.save_message({"handle": "second_user", "message": "What's up?"})
42 | await chat_db.save_message({"handle": "first_user", "message": "all good!"})
43 |
44 | chat_messages = await chat_db.get_all_messages()
45 | chat_db_name = await chat_db.get_name()
46 | click.secho(f" {chat_db_name} ", fg="cyan", bold=True, bg="yellow")
47 | for message in chat_messages:
48 | click.secho(f' {message["handle"]} | {message["message"]} ', fg="cyan")
49 | await chat_db.clear_db()
50 |
51 | asyncio.run(main())
--------------------------------------------------------------------------------
/CH_04_03.py:
--------------------------------------------------------------------------------
1 | import asyncio
2 |
3 | from quart import Quart, websocket, render_template
4 | from quart import g
5 |
6 |
7 | app = Quart(__name__)
8 |
9 |
10 | connections = set()
11 |
12 |
13 | @app.websocket("/ws")
14 | async def ws():
15 | connections.add(websocket._get_current_object())
16 | try:
17 | while True:
18 | message = await websocket.receive()
19 | send_coroutines = [connection.send(message) for connection in connections]
20 | await asyncio.gather(*send_coroutines)
21 | finally:
22 | connections.remove(websocket._get_current_object())
23 |
24 |
25 | @app.route("/")
26 | async def chat():
27 | return await render_template("chat.html")
28 |
29 |
30 | app.run(use_reloader=True, port=3000)
31 |
--------------------------------------------------------------------------------
/CH_04_05.py:
--------------------------------------------------------------------------------
1 | import json
2 | import asyncio
3 | from pprint import pp
4 |
5 | from quart import Quart, websocket, render_template
6 | from quart import g
7 |
8 | from quart_redis import RedisHandler, get_redis
9 |
10 | app = Quart(__name__)
11 |
12 | app.config["REDIS_URI"] = "redis://localhost/"
13 | redis_handler = RedisHandler(app)
14 | connections = set()
15 |
16 | import json
17 | import asyncio
18 |
19 | import click
20 | import aioredis
21 |
22 |
23 | class Chat:
24 | def __init__(self, room_name):
25 | self.room_name = room_name
26 |
27 | async def start_db(self):
28 | self.redis = await aioredis.create_redis_pool("redis://localhost")
29 | await self.redis.set("room_name", self.room_name)
30 |
31 | async def save_message(self, message_json):
32 | room_name = await self.redis.get("room_name")
33 | await self.redis.rpush(room_name, message_json)
34 |
35 | async def clear_db(self):
36 | await self.redis.flushall()
37 |
38 | async def get_all_messages(self):
39 | room_name = await self.redis.get("room_name")
40 | message_jsons = await self.redis.lrange(room_name, 0, -1, encoding="utf-8")
41 | messages = []
42 | for message in message_jsons:
43 | message_dictionary = json.loads(message)
44 | messages.append(message_dictionary)
45 |
46 | return messages
47 |
48 | async def get_name(self):
49 | return await self.redis.get("room_name", encoding="utf-8")
50 |
51 |
52 | chat_db = Chat("chat_room")
53 |
54 |
55 | @app.before_serving
56 | async def init_db():
57 | await chat_db.start_db()
58 |
59 |
60 | @app.websocket("/ws")
61 | async def ws():
62 | connections.add(websocket._get_current_object())
63 | try:
64 | while True:
65 | message = await websocket.receive()
66 | # save the message
67 | send_coroutines = [connection.send(message) for connection in connections]
68 | await asyncio.gather(*send_coroutines)
69 | finally:
70 | connections.remove(websocket._get_current_object())
71 |
72 |
73 | @app.route("/")
74 | async def chat():
75 | redis = get_redis()
76 | messages = [] # replace the empty list with your code
77 | return await render_template("chat_redis.html", messages=messages)
78 |
79 |
80 | app.run(use_reloader=True, port=3000)
81 |
--------------------------------------------------------------------------------
/CH_04_06.py:
--------------------------------------------------------------------------------
1 | import json
2 | import asyncio
3 | from pprint import pp
4 |
5 | from quart import Quart, websocket, render_template
6 | from quart import g
7 |
8 | from quart_redis import RedisHandler, get_redis
9 |
10 | app = Quart(__name__)
11 |
12 | app.config["REDIS_URI"] = "redis://localhost/"
13 | redis_handler = RedisHandler(app)
14 | connections = set()
15 |
16 | import json
17 | import asyncio
18 |
19 | import click
20 | import aioredis
21 |
22 |
23 | class Chat:
24 | def __init__(self, room_name):
25 | self.room_name = room_name
26 |
27 | async def start_db(self):
28 | self.redis = await aioredis.create_redis_pool("redis://localhost")
29 | await self.redis.set("room_name", self.room_name)
30 |
31 | async def save_message(self, message_json):
32 | room_name = await self.redis.get("room_name")
33 | await self.redis.rpush(room_name, message_json)
34 |
35 | async def clear_db(self):
36 | await self.redis.flushall()
37 |
38 | async def get_all_messages(self):
39 | room_name = await self.redis.get("room_name")
40 | message_jsons = await self.redis.lrange(room_name, 0, -1, encoding="utf-8")
41 | messages = []
42 | for message in message_jsons:
43 | message_dictionary = json.loads(message)
44 | messages.append(message_dictionary)
45 |
46 | return messages
47 |
48 | async def get_name(self):
49 | return await self.redis.get("room_name", encoding="utf-8")
50 |
51 |
52 | chat_db = Chat("chat_room")
53 |
54 |
55 | @app.before_serving
56 | async def init_db():
57 | await chat_db.start_db()
58 |
59 |
60 | @app.websocket("/ws")
61 | async def ws():
62 | connections.add(websocket._get_current_object())
63 | try:
64 | while True:
65 | message = await websocket.receive()
66 | await chat_db.save_message(message)
67 | send_coroutines = [connection.send(message) for connection in connections]
68 | await asyncio.gather(*send_coroutines)
69 | finally:
70 | connections.remove(websocket._get_current_object())
71 |
72 |
73 | @app.route("/")
74 | async def chat():
75 | redis = get_redis()
76 | messages = await chat_db.get_all_messages()
77 | return await render_template("chat_redis.html", messages=messages)
78 |
79 |
80 | app.run(use_reloader=True, port=3000)
81 |
--------------------------------------------------------------------------------
/NOTICE.md:
--------------------------------------------------------------------------------
1 | Copyright 2021 LinkedIn Corporation
2 | All Rights Reserved.
3 |
4 | Licensed under the LinkedIn Learning Exercise File License (the "License").
5 | See LICENSE in the project root for license information.
6 |
7 | ATTRIBUTIONS:
8 |
9 | redis
10 | https://github.com/redis/redis
11 | Copyright (c) 2006-2020, Salvatore Sanfilippo
12 | Licenes: BSD-3
13 | https://opensource.org/licenses/BSD-3-Clause
14 |
15 | Please note, this project may automatically load third party code from external
16 | repositories (for example, NPM modules, Composer packages, or other dependencies).
17 | If so, such third party code may be subject to other license terms than as set
18 | forth above. In addition, such third party code may also depend on and load
19 | multiple tiers of dependencies. Please review the applicable licenses of the
20 | additional dependencies.
21 |
22 | =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
23 |
24 | BSD-3 License:
25 |
26 | Copyright (c) 2006-2020, Salvatore Sanfilippo
27 | All rights reserved.
28 |
29 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
30 |
31 | * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
32 | * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
33 | * Neither the name of Redis nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
34 |
35 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Async Python Foundations: Applied Concepts
2 | This is the repository for the LinkedIn Learning course Async Python Foundations: Applied Concepts. The full course is available from [LinkedIn Learning][lil-course-url].
3 |
4 | ![Async Python Foundations: Applied Concepts][lil-thumbnail-url]
5 |
6 | If you were cooking a multicourse meal, would you prep one thing at a time? Put bread in the oven, wait. Warm the soup on the stove, wait. Then the main course. Wouldn’t it be more efficient to spend time prepping other food instead of waiting on tasks that don’t need your immediate attention? In the same way that having multiple things happen at the same time leads to faster meal prep, having multiple things happen in Python—or using an asynchronous approach—can be leveraged to boost application performance and make your Python programs extremely efficient. In this course, Ronnie Sheer gives you the tools to use async Python to solve real-world problems, gain familiarity with the async Python ecosystem, complete challenges with working examples, and become a more attractive candidate for engineering positions. If you’re an experienced Python user looking to take async Python from theory to practice, check out this hands-on course.
7 |
8 | ## Easy setup with Gitpod (cloud workspace).
9 | [](https://gitpod.io/#/https://github.com/LinkedInLearning/async-python-foundations-applied-concepts-2422322)
10 |
11 | ## Local setup
12 | ## Downloads
13 | * Visit [python.org](https://www.python.org/) and download Python for your operating system.
14 | * Visit [redis.io](https://redis.io/download) and download Redis for your operating system.
15 |
16 | ## Setting up a virtual environment
17 | 1. Create a virtual environment with the following command:
18 | ```bash
19 | python3 -m venv venv
20 | ```
21 | 1. Activate the virtual environment.
22 | 1. Linux and Mac
23 | ```bash
24 | source venv/bin/activate
25 | ```
26 | 2. Windows
27 | ```bash
28 | venv\Scripts\activate
29 | ```
30 | > :warning: *If you encounter an error using Windows PowerShell, try opening another shell with the "Run as Administrator" option.*
31 | 1. Install the required packages.
32 | ```
33 | pip install -r requirements.txt
34 | ```
35 |
36 | ## Every time you start a terminal session
37 | 1. Navigate to the course directory.
38 |
39 | 1. Activate the virtual environment.
40 | 1. Linux and Mac
41 | ```bash
42 | source venv/bin/activate
43 | ```
44 | 2. Windows
45 | ```bash
46 | venv\Scripts\activate
47 | ```
48 |
49 | ## Running a single exercise file
50 | > :warning: *Make sure that you have set up your environment according to the instructions above.*
51 |
52 | 1. Python
53 | ```bash
54 | python CH_01_04_end.py
55 | ```
56 | 1. Starting redis(in separate terminal)
57 | ```bash
58 | redis-server
59 | ```
60 |
61 |
62 | ### Instructor
63 |
64 | Ronnie Sheer
65 |
66 | Check out my other courses on [LinkedIn Learning](https://www.linkedin.com/learning/instructors/ronnie-sheer).
67 |
68 | [lil-course-url]: https://www.linkedin.com/learning/async-python-foundations-applied-concepts
69 | [lil-thumbnail-url]: https://cdn.lynda.com/course/2422322/2422322-1630605594091-16x9.jpg
70 |
71 |
--------------------------------------------------------------------------------
/dump.rdb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LinkedInLearning/async-python-foundations-applied-concepts-2422322/753e508de6929528e1c13b7ac785820547742a06/dump.rdb
--------------------------------------------------------------------------------
/repo_data.json:
--------------------------------------------------------------------------------
1 | [{"name": "Python", "avatar_url": "https://avatars.githubusercontent.com/u/1525981?v=4"}, {"name": "Django", "avatar_url": "https://avatars.githubusercontent.com/u/27804?v=4"}, {"name": "Pallets", "avatar_url": "https://avatars.githubusercontent.com/u/16748505?v=4"}]
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | aiofiles==0.7.0
2 | aiohttp==3.7.4.post0
3 | aioredis==1.3.1
4 | appdirs==1.4.4
5 | async-timeout==3.0.1
6 | attrs==21.1.0
7 | black==21.5b1
8 | blinker==1.4
9 | certifi==2021.5.30
10 | chardet==4.0.0
11 | click==7.1.2
12 | Flask==2.0.0
13 | h11==0.12.0
14 | h2==4.0.0
15 | hiredis==2.0.0
16 | hpack==4.0.0
17 | Hypercorn==0.11.2
18 | hyperframe==6.0.1
19 | idna==2.10
20 | itsdangerous==2.0.1
21 | Jinja2==3.0.1
22 | MarkupSafe==2.0.1
23 | multidict==5.1.0
24 | mypy-extensions==0.4.3
25 | pathspec==0.8.1
26 | priority==1.3.0
27 | Quart==0.15.0
28 | quart-redis==0.1.0
29 | regex==2021.4.4
30 | requests==2.25.1
31 | toml==0.10.2
32 | typing-extensions==3.10.0.0
33 | websockets==9.1
34 | urllib3==1.26.6
35 | Werkzeug==2.0.1
36 | wsproto==1.0.0
37 | yarl==1.6.3
38 |
--------------------------------------------------------------------------------
/show_repos.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Document
8 |
9 |
10 |
11 |
16 |
17 |
--------------------------------------------------------------------------------
/templates/base.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/templates/chat.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Chat App
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
Chat
20 |
21 |
22 |
23 |
24 |
25 |
26 |
31 |
32 |
33 |
34 |
62 |
63 |
--------------------------------------------------------------------------------
/templates/chat_redis.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Chat App
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
Chat
20 |
21 |
22 | {% for message in messages %}
23 |
24 | {{message.handle }} |
25 | {{message.message }} |
26 |
27 | {% else %}
28 | {% endfor %}
29 |
30 |
31 |
32 |
33 |
38 |
39 |
40 |
41 |
71 |
72 |
--------------------------------------------------------------------------------