mirror of
https://github.com/vinta/awesome-python.git
synced 2026-10-02 08:23:10 +08:00
Merge branch 'fix/todo-pass2'
This commit is contained in:
+2
-1
@@ -755,8 +755,9 @@ def build(repo_root: Path) -> None:
|
||||
if parent_category:
|
||||
breadcrumbs.append((parent_category["name"], category_public_url(parent_category)))
|
||||
breadcrumbs.append((category["name"], category_url))
|
||||
page_entries = [entry for group in entry_groups for entry in group["entries"]] if entry_groups else entries
|
||||
category_json_ld = json.dumps(
|
||||
build_category_json_ld(category_title.removesuffix(" - Awesome Python"), category_url, category_description, entries, breadcrumbs),
|
||||
build_category_json_ld(category_title.removesuffix(" - Awesome Python"), category_url, category_description, page_entries, breadcrumbs),
|
||||
ensure_ascii=False,
|
||||
).replace("</", "<\\/")
|
||||
(page_dir / "index.html").write_text(
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Validate API input and config with Pydantic, the Python data validation library built on type hints. Use Pandera for dataframes, jsonschema for JSON Schema.
|
||||
Declare API input and config as type hints, and Pydantic validates them. Pandera brings Python data validation to dataframes, and jsonschema covers JSON Schema.
|
||||
|
||||
How to choose:
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Most code should handle time zones with zoneinfo and pick python-dateutil, the Python date library for parsing date strings and adding months.
|
||||
Handle time zones with the built-in zoneinfo. Parsing date strings and adding months is where a Python date library earns its install: python-dateutil.
|
||||
|
||||
How to choose:
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
PDF, Word, and Excel files open with pypdf, python-docx, and openpyxl, a Python file format library for each. MarkItDown turns all three into Markdown.
|
||||
pypdf reads PDFs, python-docx Word files, and openpyxl Excel sheets: one Python file format library per format. MarkItDown turns any of them into Markdown.
|
||||
|
||||
How to choose:
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
A 2D game needs a loop: write your own with pygame-ce, the Python game development library, or let Arcade run it. Panda3D does 3D, and Ren'Py visual novels.
|
||||
pygame-ce leaves the 2D game loop to you, while Arcade runs it for you. For 3D, Panda3D covers Python game development, and Ren'Py builds visual novels.
|
||||
|
||||
How to choose:
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
If your templates should hold no Python code, Jinja fits: a Python template engine whose sandbox also renders untrusted templates. Mako embeds plain Python.
|
||||
Whether templates may hold Python code splits the Python template engines: Jinja keeps code out and sandboxes untrusted templates, Mako embeds plain Python.
|
||||
|
||||
How to choose:
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
For a Django project, pick Django REST framework or Django Ninja. Outside Django, build on FastAPI, a Python API framework based on type hints.
|
||||
Outside Django, declare each FastAPI endpoint with type hints. Django projects add a Python API framework on top instead: Django REST framework or Django Ninja.
|
||||
|
||||
How to choose:
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
When you want an ORM, an admin, and auth built in, use Django. Flask, a smaller Python web framework, gives you a core you extend.
|
||||
How much should a Python web framework build in? Django comes with an ORM, an admin, and auth; Flask gives you a small core to extend.
|
||||
|
||||
How to choose:
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
In production, Gunicorn serves WSGI apps like Django, and Uvicorn serves ASGI apps like FastAPI. Waitress, a pure-Python web server, runs WSGI apps on Windows.
|
||||
In production, Gunicorn serves WSGI apps like Django, and Uvicorn ASGI apps like FastAPI. Need a Python web server on Windows? Waitress runs WSGI there.
|
||||
|
||||
How to choose:
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
With Django, Channels is the pick; with Flask and Socket.IO clients, Flask-SocketIO. Standalone apps run on websockets, a Python WebSocket library.
|
||||
Your Python WebSocket library follows your framework: Channels on Django, Flask-SocketIO on Flask with Socket.IO clients. websockets needs no framework.
|
||||
|
||||
How to choose:
|
||||
|
||||
|
||||
@@ -84,12 +84,7 @@ def collect_names(readme_text: str) -> list[str]:
|
||||
|
||||
def fetch_clickpy(names: list[str]) -> dict[str, int]:
|
||||
in_list = ", ".join(f"'{name}'" for name in names)
|
||||
query = (
|
||||
"SELECT project, sum(count) AS downloads "
|
||||
"FROM pypi.pypi_downloads_per_day "
|
||||
f"WHERE project IN ({in_list}) AND date >= today() - 30 "
|
||||
"GROUP BY project FORMAT JSON"
|
||||
)
|
||||
query = f"SELECT project, sum(count) AS downloads FROM pypi.pypi_downloads_per_day WHERE project IN ({in_list}) AND date >= today() - 30 GROUP BY project FORMAT JSON"
|
||||
resp = httpx.post(CLICKPY_URL, content=query, timeout=60)
|
||||
resp.raise_for_status()
|
||||
return {row["project"]: int(row["downloads"]) for row in resp.json()["data"]}
|
||||
|
||||
@@ -145,7 +145,9 @@ function applyFilters() {
|
||||
|
||||
collapseAll();
|
||||
|
||||
rows.forEach(function (row) {
|
||||
// Number rows in their sorted DOM order, not the load order `rows` keeps
|
||||
const orderedRows = tbody ? tbody.querySelectorAll("tr.row") : rows;
|
||||
orderedRows.forEach(function (row) {
|
||||
let show = true;
|
||||
|
||||
if (query) {
|
||||
|
||||
@@ -241,7 +241,7 @@
|
||||
<td class="col-arrow"><span class="arrow">→</span></td>
|
||||
</tr>
|
||||
{% if entry.description %}
|
||||
<tr class="desc-row" aria-hidden="true" hidden>
|
||||
<tr class="desc-row" hidden>
|
||||
<td class="col-num"></td>
|
||||
<td colspan="6">
|
||||
<div class="desc-text">{{ entry.description | safe }}</div>
|
||||
|
||||
@@ -679,6 +679,39 @@ class TestBuild:
|
||||
{"@type": "ListItem", "position": 2, "name": "Widgets", "item": "https://awesome-python.com/categories/widgets/"},
|
||||
]
|
||||
|
||||
def test_category_json_ld_follows_page_order(self, tmp_path):
|
||||
readme = textwrap.dedent("""\
|
||||
# Awesome Python
|
||||
|
||||
Intro.
|
||||
|
||||
## Projects
|
||||
|
||||
**Tools**
|
||||
|
||||
## Widgets
|
||||
|
||||
_Widget libraries._
|
||||
|
||||
- [zeta](https://example.com/zeta) - Listed first.
|
||||
- [alpha](https://example.com/alpha) - Listed second.
|
||||
|
||||
# Contributing
|
||||
|
||||
Help!
|
||||
""")
|
||||
(tmp_path / "README.md").write_text(readme, encoding="utf-8")
|
||||
self._copy_real_templates(tmp_path)
|
||||
build(tmp_path)
|
||||
|
||||
category_html = (tmp_path / "website" / "output" / "categories" / "widgets" / "index.html").read_text(encoding="utf-8")
|
||||
marker = '<script type="application/ld+json">'
|
||||
start = category_html.index(marker) + len(marker)
|
||||
data = json.loads(category_html[start : category_html.index("</script>", start)])
|
||||
collection = next(node for node in data["@graph"] if node["@type"] == "CollectionPage")
|
||||
items = collection["mainEntity"]["itemListElement"]
|
||||
assert [(item["position"], item["name"]) for item in items] == [(1, "zeta"), (2, "alpha")]
|
||||
|
||||
def test_group_page_falls_back_to_default_description_in_json_ld(self, tmp_path):
|
||||
readme = textwrap.dedent("""\
|
||||
# T
|
||||
|
||||
Reference in New Issue
Block a user