forked from tecladocode/complete-python-course
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(ebook): start ebook structure for this course
- Loading branch information
Showing
33 changed files
with
345 additions
and
148 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
# Contributing | ||
|
||
## Building the docs | ||
|
||
Write your documentation inside `course_contents`. | ||
|
||
To build and serve the documentation, use this command: | ||
|
||
```bash | ||
watchmedo shell-command --patterns="*.md" --recursive course_contents --command="cog -r mkdocs.yml" | ||
``` | ||
|
||
This runs the `cog -r mkdocs.yml` command every time there is a markdown file change. | ||
|
||
**note**: at the moment there is [a bug](https://github.com/gorakhargosh/watchdog/issues/346) in `watchmedo` that triggers the shell command twice on file modification, or potentially a few times on file creation. It doesn't affect us majorly. | ||
|
||
### Why do we need to use `cog` to build the docs before serving? | ||
|
||
The documentation system we use for this e-book, `mkdocs`, has limitation regarding slugs: all slugs are calculated from the filesystem paths. | ||
|
||
This means that if we simply serve the docs from `course_contents`, all our slugs would be numbered (e.g. `1_intro/lectures/3_variables_printing/`). | ||
|
||
To avoid this, we first run a build step (in `content.py`, `build_and_get_yaml_contents()`) that copies all the non-hidden sections and lectures to a `build` folder, and then we can serve the documentation from there. | ||
|
||
This is a bit "hacky", and we must remember to run `cog` on the `mkdocs.yml` file if we want to see our updated documentation! | ||
|
||
## Writing documentation README files | ||
|
||
There are a few attributes for each `README.md` file that we can use. | ||
|
||
In section files: | ||
|
||
- `group: str` will try to place sections with the same group name inside a tabbed navigation. | ||
- `hidden: true | false` will hide the section and its lectures, and not include any of them in the build. | ||
|
||
In lecture files: | ||
|
||
- `hidden: true | false` will hide the lecture and not include it in the build. Other lectures in the same section are unaffected. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
import glob | ||
import json | ||
import pathlib | ||
import shutil | ||
import string | ||
import markdown | ||
from bs4 import BeautifulSoup | ||
|
||
def get_grouped_sections(root: str = "course_contents") -> dict[str, list]: | ||
sections = get_all_sections_with_content(root) | ||
grouped_sections = {} | ||
for section in sections: | ||
group = section["index"]["group"] | ||
if not group: | ||
raise RuntimeError("Not all sections part of a group.") | ||
if group: | ||
grouped_sections.setdefault(group, []).append(section) | ||
else: | ||
grouped_sections.setdefault("default", []).append(section) | ||
return grouped_sections | ||
|
||
def get_all_sections_with_content(root: str = "course_contents"): | ||
root_path = pathlib.Path(root) | ||
section_folders = [folder.name for folder in root_path.iterdir() if folder.is_dir() and folder.name[0] in string.digits] | ||
sections = sorted(section_folders, key=lambda e: int(str(e).split("_")[0])) | ||
for section in sections: | ||
section = root_path / section | ||
section_index = get_lecture_content(section / "README.md") | ||
if not section_index.get("title") or section_index.get("hidden"): | ||
continue | ||
lectures = get_section_lectures(section) | ||
lecture_contents = [get_lecture_content(lecture) for lecture in lectures] | ||
non_hidden_lectures = [lecture for lecture in lecture_contents if not lecture.get("hidden")] | ||
yield {"index": section_index, "lectures": non_hidden_lectures} | ||
|
||
|
||
def get_section_lectures(folder: pathlib.Path | str) -> list[str]: | ||
"""Return a list of pathlib.Path objects for all lectsures in a section folder""" | ||
lecture_path = pathlib.Path(folder) / "lectures" | ||
return sorted([folder for folder in lecture_path.glob("*/README.md")]) | ||
|
||
|
||
def get_lecture_content(lecture_path: pathlib.Path, root_path: str | pathlib.Path = "course_contents") -> dict[str, str]: | ||
"""Return a dictionary of lecture content. | ||
Return a dictionary with the following keys: | ||
- title, the h1 from the markdown file | ||
- summary from the markdown metadata | ||
- group, to group sections together, optional | ||
- hidden, to hide entire sections, optional | ||
- filename, the name of the markdown file | ||
- full_path, the full path to the markdown file | ||
- order, the order of the lecture in the section (defaults to file name) | ||
- path, the path to this file relative to the root""" | ||
with open(lecture_path) as f: | ||
content = f.read() | ||
md = markdown.Markdown(extensions=["meta"]) | ||
html = md.convert(content) | ||
soup = BeautifulSoup(html, "html.parser") | ||
return { | ||
"title": soup.find("h1").text, | ||
"summary": md.Meta.get("summary", ""), | ||
"group": md.Meta.get("group", [""])[0], | ||
"hidden": md.Meta.get("hidden", [""])[0] == "true", | ||
"filename": lecture_path.name, | ||
"full_path": lecture_path.absolute(), | ||
"order": md.Meta.get("order", [lecture_path.name])[0], | ||
"path": lecture_path.relative_to(root_path), | ||
} | ||
|
||
|
||
def get_grouped_build_sections(root: str = "build") -> dict[str, list]: | ||
sections = build_and_get_yaml_contents(root) | ||
grouped_sections = {} | ||
for section in sections: | ||
group = section["index"]["group"] | ||
if not group: | ||
raise RuntimeError("Not all sections part of a group.") | ||
if group: | ||
grouped_sections.setdefault(group, []).append(section) | ||
else: | ||
grouped_sections.setdefault("default", []).append(section) | ||
return grouped_sections | ||
|
||
|
||
def build_and_get_yaml_contents(build_path: str = "build"): | ||
# Delete contents of the build directory | ||
shutil.rmtree(build_path, ignore_errors=True) | ||
sections = get_all_sections_with_content() | ||
for section in sections: | ||
# Strip the leading numbers of the section folder | ||
old_section_name = section["index"]["full_path"].parent.name | ||
section_name = "_".join(old_section_name.split("_")[1:]) | ||
# Create a directory in the build folder matching the section_name | ||
pathlib.Path(build_path, section_name).mkdir(parents=True, exist_ok=True) | ||
# Copy the README.md file from the original section to the new directory | ||
shutil.copyfile(section["index"]["full_path"], pathlib.Path(build_path, section_name, "README.md")) | ||
# Copy the lecture folders to the new directory | ||
section["lectures"] = list(copy_lectures_to_build_path(section, section_name, build_path=build_path)) | ||
# Update the section index to point to the new path | ||
section["index"]["full_path"] = pathlib.Path(build_path, section_name, "README.md").absolute() | ||
section["index"]["path"] = pathlib.Path(build_path, section_name, "README.md").relative_to(build_path) | ||
yield section | ||
|
||
|
||
def copy_lectures_to_build_path(section: dict, new_section_name: str, build_path: str = "build"): | ||
for lecture in section["lectures"]: | ||
lecture_name = "_".join(lecture["full_path"].parent.name.split("_")[1:]) | ||
pathlib.Path(build_path, new_section_name, lecture_name).mkdir(parents=True, exist_ok=True) | ||
shutil.copyfile(lecture["full_path"], pathlib.Path(build_path, new_section_name, lecture_name, "README.md")) | ||
lecture["full_path"] = pathlib.Path(build_path, new_section_name, lecture_name, "README.md").absolute() | ||
lecture["path"] = pathlib.Path(build_path, new_section_name, lecture_name, "README.md").relative_to(build_path) | ||
yield lecture | ||
|
||
|
||
if __name__ == "__main__": | ||
section_yaml = get_grouped_build_sections() | ||
print(list(section_yaml)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
19 changes: 19 additions & 0 deletions
19
course_contents/13_async_development/lectures/01_code_samples/README.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
--- | ||
hidden: false | ||
--- | ||
|
||
# Code samples for this section | ||
|
||
Hey, welcome back! | ||
|
||
This section won't have code available on our online editor, but instead all the code we'll write here is available on the GitHub page. | ||
|
||
Again, don't read through the code before going through some of the videos. The code is here for you to check as you progress through the section. Otherwise, it could get a bit confusing! | ||
|
||
Here's a link to the code in this section: https://github.com/tecladocode/complete-python-course/tree/master/course_contents/13_async_development/sample_code | ||
|
||
As always, if you have any questions please ask away in the Course Q&A. | ||
|
||
Happy coding! | ||
|
||
Jose—your instructor |
27 changes: 27 additions & 0 deletions
27
course_contents/13_async_development/lectures/02_async_terms_glossary/README.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
--- | ||
hidden: true | ||
--- | ||
|
||
# Glossary of terms used in concurrency | ||
|
||
Hey there! Welcome to one of the most advanced topics in Python. A lot of developers are scared of what we're going to learn in this section. | ||
|
||
You shouldn't be scared! | ||
|
||
We'll start at the fundamentals, and build out our knowledge of asynchronous development from the ground up. | ||
|
||
Throughout the section we'll learn more about what these terms mean, but it can be helpful to have a short glossary of terms just in case you want to come back and remind yourself. | ||
|
||
- **Synchronous**: actions that happen one after another. Programming as we've seen it until now is synchronous, because each line executes after the previous one. | ||
- **Asynchronous**: actions that don't necessary happen after one another, or that can happen in arbitrary order ("without synchrony"). | ||
- **Concurrency**: The ability of our programs to run things in different order every time the program runs, without affecting the final outcome. | ||
- **Parallelism**: Running two or more things at the same time. | ||
- **Thread**: A line of code execution that can run in one of your computer's cores. | ||
- **Process**: One of more threads and the resources they need (e.g. network connection, mouse pointer, hard drive access, or even the core(s) in which the thread(s) run). | ||
- **GIL**: A key, critical, important resource in any Python program. Only one is created per Python process, so it's unique in each. | ||
|
||
Take it slowly through this section, and ask questions in the Course Q&A as required. We're here to help! | ||
|
||
Kind regards, | ||
|
||
Jose |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,7 @@ | ||
--- | ||
group: Practical Python | ||
hidden: true | ||
--- | ||
# Interacting with APIs | ||
|
||
## Requirements | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.