Merge branch 'docs/new-entry-intros'

This commit is contained in:
Vinta Chen
2026-11-08 22:42:13 +08:00
8 changed files with 31 additions and 3 deletions
+2
View File
@@ -58,6 +58,7 @@ PICK_ALIASES = {
"Jupyter Notebook": "notebook",
"Material for MkDocs": "mkdocs-material",
"MCP Python SDK": "mcp",
"Monty": "pydantic-monty",
"OpenAI Agents SDK": "openai-agents",
"OpenCV": "opencv-python",
"Playwright": "playwright-python",
@@ -69,6 +70,7 @@ PICK_ALIASES = {
"Sherlock": "sherlock-project",
"Silk": "django-silk",
"Surprise": "scikit-surprise",
"Temporal": "temporalio",
"The Algorithms - Python": "thealgorithms",
"Unfold": "django-unfold",
"Vega-Altair": "altair",
@@ -6,6 +6,7 @@ How to choose:
- Agents on Claude, OpenAI, or Gemini models: Claude Agent SDK, OpenAI Agents SDK, or Google ADK
- Skills for your coding agent: Django AI Skills, Sentry Skills, or Trail of Bits Skills
- MCP servers and clients: MCP Python SDK, or FastMCP to compose and proxy servers
- Sandboxing AI-written code: Monty
- A personal assistant in your chat apps: Hermes Agent, or AstrBot for QQ, Feishu, and DingTalk
- Prompts tuned against a metric: DSPy
- Agent data: Instructor for structured output, LlamaIndex for RAG, Mem0 for memory
@@ -32,6 +33,8 @@ CrewAI splits an app into Flows, which manage state and control execution, and C
The MCP Python SDK reads each tool's schema [from its type hints and docstring](https://py.sdk.modelcontextprotocol.io/get-started/first-steps/), so you write no JSON Schema or request handlers. FastMCP wrote the high-level API that the SDK [took in](https://gofastmcp.com/getting-started/welcome). The standalone project is a fuller framework: it can [mount several servers into one](https://gofastmcp.com/servers/composition) and [proxy another MCP server](https://gofastmcp.com/servers/providers/proxy) through your own.
Monty is [a Python sandbox written in Rust for code written by AI](https://pydantic.dev/docs/monty/). It [runs Code Mode in Pydantic AI](https://pydantic.dev/docs/ai/harness/code-mode/), where the model writes one Python program that calls your tools, instead of one tool call per action. The sandbox has no filesystem, environment variables, or network, so the code reaches your host only through the functions and mounts you pass in. [Open a pool of workers and check out a session](https://pydantic.dev/docs/monty/quickstart/python/), setting memory and time limits on the checkout. A host function runs with your process's full authority, so [validate its arguments](https://pydantic.dev/docs/monty/concepts/security/) like any untrusted input. Monty implements only enough Python for a model to express what it wants, so [turn on type checking](https://pydantic.dev/docs/monty/limitations/) to catch most unsupported code before it runs.
Hermes Agent talks to you on Telegram, Discord, Slack, WhatsApp, and Signal [from one gateway process](https://github.com/NousResearch/hermes-agent), and writes itself new skills as it works. Since it runs commands for whoever it lets in, allow only your own account on its gateway with an [allowlist](https://hermes-agent.nousresearch.com/docs/user-guide/security#user-authorization-gateway), and leave approval prompts on for dangerous commands. AstrBot covers chat apps such as QQ, Feishu, and DingTalk. It's AGPL, while Hermes Agent is MIT.
DSPy has you write [structured signatures, not prompts](https://dspy.ai/current/), and its optimizers tune the prompts for you. Before they can, [define a metric and score a baseline](https://dspy.ai/current/getting-started/metrics/) to beat.
@@ -8,6 +8,8 @@ How to choose:
- Rerunning or reloading code when files change: watchfiles
- Your own handler for each created, modified, or moved file: watchdog
- Telling text file types apart, like source code from config files: Magika
- Listing, globbing, and copying files on cloud or remote storage: fsspec
- Streaming one large remote file through a drop-in `open()`: smart_open
If you aren't sure which pathlib class you need, the docs say [`Path` is most likely it](https://docs.python.org/3/library/pathlib.html): it makes a concrete path for the platform your code runs on. [Join paths with the `/` operator](https://docs.python.org/3/library/pathlib.html#basic-use), and list a directory with `iterdir()` or `glob()`.
@@ -20,3 +22,7 @@ watchfiles is built for [file watching and code reload](https://watchfiles.helpm
watchdog gives you [an API and a shell tool](https://python-watchdog.readthedocs.io/en/latest/) for monitoring directories. Its [quickstart](https://python-watchdog.readthedocs.io/en/latest/quickstart.html) has you subclass `FileSystemEventHandler`, override methods like `on_created()` and `on_modified()`, schedule the handler on an `Observer`, and start that thread. An observer skips subdirectories unless you pass `recursive=True` to `schedule()`.
Magika runs [a compact deep-learning model](https://securityresearch.google/magika/core-concepts/how-magika-works/) on a few chunks of a file, without loading large files entirely into memory. On its own test set, it [outperforms existing approaches, especially on textual content types](https://github.com/google/magika#highlights). Create a `Magika()` instance, then [call `identify_path()` on a file or `identify_bytes()` on bytes you already have](https://securityresearch.google/magika/cli-and-bindings/python/#instantiating-magika). Check `result.ok` before you read `result.output.label` or `result.output.mime_type`.
fsspec gives you [one API for local, remote, and embedded file systems](https://filesystem-spec.readthedocs.io/en/latest/), and it works the same whatever the storage backend. Get a file system by protocol name, like `fsspec.filesystem("s3")`, then call methods that [follow posix naming and the `os` module](https://filesystem-spec.readthedocs.io/en/latest/usage.html#use-a-file-system): `ls()`, `glob()`, `cp()`, and `rm()`. For one file, [`fsspec.open()` picks the backend from the URL](https://filesystem-spec.readthedocs.io/en/latest/usage.html#higher-level), and opens the file only when you enter its `with` block.
smart_open is [a drop-in replacement for Python's built-in `open()`](https://github.com/piskvorky/smart_open#what) that streams very large files from and to S3, GCS, Azure Blob Storage, HDFS, HTTP, and SFTP. Import its `open` in place of the built-in, and pass it a URL like `s3://bucket/key`. A file name ending in `.gz`, `.bz2`, or `.zst` gets [compressed or decompressed on the fly](https://github.com/piskvorky/smart_open#compression-handling). To use your own credentials or settings, [pass the storage service's client object in `transport_params`](https://github.com/piskvorky/smart_open#transport-specific-options).
@@ -7,6 +7,7 @@ How to choose:
- Your Python functions as pipelines, with tasks created at runtime: Prefect
- Pipelines built around the data assets they produce, like tables and models: Dagster
- A simple loop of periodic jobs in one script: schedule
- Long-running business processes that survive crashes, with retries: Temporal
APScheduler [schedules your Python code to run later](https://github.com/agronholm/apscheduler/blob/3.x/README.rst), once or periodically. It runs inside your existing application, not as a service. Give each job [a trigger](https://apscheduler.readthedocs.io/en/latest/userguide.html#choosing-the-right-scheduler-job-store-s-executor-s-and-trigger-s): `date` runs it once, `interval` at fixed intervals, and `cron` at set times of day. Jobs [live in memory by default](https://apscheduler.readthedocs.io/en/latest/userguide.html#basic-concepts). When they must survive restarts and crashes, add a persistent job store.
@@ -18,4 +19,6 @@ Prefect [turns your Python functions into data pipelines](https://docs.prefect.i
Dagster is [a data orchestrator built for data engineers](https://docs.dagster.io/), with lineage and observability built in. You [declare data assets like tables, datasets, and ML models as Python functions](https://github.com/dagster-io/dagster). Dagster runs them at the right time to keep them up to date. If you're just starting out, its docs [strongly recommend assets rather than ops](https://docs.dagster.io/guides/build/ops). Run assets on [a cron schedule](https://docs.dagster.io/guides/automate/schedules).
In an orchestrator, make every task safe to run twice. Airflow [can retry a failed task](https://airflow.apache.org/docs/apache-airflow/stable/best-practices.html#creating-a-task), so its docs say a task should produce the same outcome on every re-run. Prefect tasks are [retryable units of work](https://docs.prefect.io/v3/concepts/tasks) too.
Temporal [runs your application code as a Durable Execution](https://docs.temporal.io/evaluate/understanding-temporal#durable-execution). Once a workflow starts, it runs to completion, even when that takes a year. If its process crashes, another process picks it up where it stopped. With the Temporal Python SDK, [put `@activity.defn` on each function that does one action, like calling an API, and `@workflow.defn` on the class that orchestrates them](https://docs.temporal.io/develop/python/set-up-your-local-python#run-hello-world-test-your-installation). Keep anything that talks to the outside world in an activity, since Temporal [reruns workflow code to rebuild its state](https://docs.temporal.io/evaluate/understanding-temporal#workflows). A failed activity [retries with backoff](https://docs.temporal.io/evaluate/understanding-temporal#activities). [A worker on your own infrastructure](https://docs.temporal.io/evaluate/understanding-temporal#workers) runs your code and asks the Temporal Service for work. [The Temporal Service is the Temporal Server plus a database](https://docs.temporal.io/temporal#temporal-platform), and you run it yourself or use Temporal Cloud.
In an orchestrator, make every task safe to run twice. Airflow [can retry a failed task](https://airflow.apache.org/docs/apache-airflow/stable/best-practices.html#creating-a-task), so its docs say a task should produce the same outcome on every re-run. Prefect tasks are [retryable units of work](https://docs.prefect.io/v3/concepts/tasks) too. Temporal [recommends idempotent activities](https://docs.temporal.io/activity-definition#idempotency), since a retried activity can run more than once.
+3
View File
@@ -7,6 +7,7 @@ How to choose:
- An app already on logging that needs structured output: structlog, which wraps it
- A script you want logging in with no setup: Loguru
- Log files that rotate, expire, and compress: Loguru
- Logs next to traces and metrics, on a hosted platform: Logfire
Every Python module [can take part in the standard library's logging](https://docs.python.org/3/library/logging.html), so your app's log holds messages from third-party packages next to your own. In each module, create a logger with [`logging.getLogger(__name__)`](https://docs.python.org/3/howto/logging.html#advanced-logging-tutorial), so logger names follow your package layout. Use `basicConfig()` for a quick setup, and move to [`dictConfig()`](https://docs.python.org/3/howto/logging.html#configuring-logging), which the docs recommend for new applications. In a library, [add no handler other than `NullHandler`](https://docs.python.org/3/howto/logging.html#configuring-logging-for-a-library) and don't log to the root logger: handlers are for the app developer to pick.
@@ -14,4 +15,6 @@ structlog logs [events in a context of key-value pairs](https://www.structlog.or
Loguru has [one logger, ready to use](https://loguru.readthedocs.io/en/latest/overview.html#ready-to-use-out-of-the-box-without-boilerplate) after `from loguru import logger`, and it writes to stderr out of the box. Instead of handlers, formatters, and filters, you configure it with [one function, `add()`](https://loguru.readthedocs.io/en/latest/overview.html#no-handler-no-formatter-no-filter-one-function-to-rule-them-all). In an app, [call `remove()` first](https://loguru.readthedocs.io/en/latest/resources/troubleshooting.html#how-do-i-create-and-configure-a-logger) to drop the default handler, then `add()` where your logs should go. Given a file path, `add()` handles [rotation, retention, and compression](https://loguru.readthedocs.io/en/latest/overview.html#easier-file-logging-with-rotation-retention-compression), and [`serialize=True`](https://loguru.readthedocs.io/en/latest/overview.html#structured-logging-as-needed) turns each message into JSON. In a library, [call `disable()` instead of `add()`](https://loguru.readthedocs.io/en/latest/overview.html#suitable-for-scripts-and-libraries), and the app using it can `enable()` your logs again.
Logfire is an [observability platform built on OpenTelemetry](https://pydantic.dev/docs/logfire/get-started/why/), so your logs show up next to the traces and metrics from the rest of your code. Unlike the standard library, structlog, and Loguru, it's a [hosted service with paid plans](https://pydantic.dev/pricing): the SDK [sends your data to Logfire, where it's stored](https://pydantic.dev/docs/logfire/get-started/first-trace/#before-you-start). You can also [export to any backend that supports OpenTelemetry](https://pydantic.dev/docs/logfire/guides/alternative-backends/). [Call `logfire.configure()`, then log with `logfire.info()` inside a `logfire.span()`](https://pydantic.dev/docs/logfire/get-started/first-trace/#or-do-it-by-hand): the span records one operation, and together they make a trace. To send the logs you already write, add [`LogfireLoggingHandler`](https://pydantic.dev/docs/logfire/integrations/logging/logging/) to the standard library's logging, [`StructlogProcessor`](https://pydantic.dev/docs/logfire/integrations/logging/structlog/) to structlog's processors before the renderer, or [`loguru_handler()`](https://pydantic.dev/docs/logfire/integrations/logging/loguru/) as a Loguru sink.
Whichever you pick, configure logging once, where your app starts, and have each module only get its logger. With the standard library, usually [only the root logger needs configuring](https://docs.python.org/3/library/logging.html), since module loggers pass their messages up to it. structlog wants [`structlog.configure()` on app initialization](https://www.structlog.org/en/latest/configuration.html), and with Loguru, [your other modules inherit the configuration](https://loguru.readthedocs.io/en/latest/resources/troubleshooting.html#how-do-i-create-and-configure-a-logger) from your entry point. Third-party packages that use the standard library's logging keep logging there. Send their messages to your output too: structlog [formats them with `ProcessorFormatter`](https://www.structlog.org/en/latest/standard-library.html#processor-formatter), and Loguru [intercepts them with a handler](https://loguru.readthedocs.io/en/latest/overview.html#entirely-compatible-with-standard-logging).
@@ -12,6 +12,7 @@ How to choose:
- C or C++ extension modules: setuptools
- Build hooks, or a layout uv-build doesn't expect: Hatchling
- Building a Poetry project without Poetry installed: poetry-core
- Taking the version and description from your module: flit_core
uv is [a single tool](https://docs.astral.sh/uv/) for Python versions, virtual environments, dependencies, and command-line tools. Start a project with `uv init`, add packages with `uv add`, and run code with `uv run`, which [checks that the lockfile and environment are up to date](https://docs.astral.sh/uv/guides/projects/#running-commands) before every run. The `uv pip` commands are [for projects not ready to move away from pip](https://docs.astral.sh/uv/pip/), not the main interface.
@@ -31,4 +32,6 @@ conda manages [packages and environments for any language](https://docs.conda.io
setuptools [builds C and C++ extension modules](https://setuptools.pypa.io/en/latest/userguide/ext_modules.html), and Hatch's docs [recommend it](https://hatch.pypa.io/latest/why/#build-backend) for them. Keep its configuration in pyproject.toml and `setup.py` [minimal, if you need one at all](https://setuptools.pypa.io/en/latest/userguide/quickstart.html#setuppy-discouraged), and don't run `setup.py` as a script.
flit_core is Flit's build backend, made for [pure Python packages with no build steps](https://flit.pypa.io/en/latest/rationale.html). List `version` and `description` under `dynamic`, and it [reads them from your module's `__version__` and docstring](https://flit.pypa.io/en/latest/pyproject_toml.html#project-metadata). To start, `flit init` [writes the pyproject.toml](https://flit.pypa.io/en/latest/) for your module.
Whichever tool writes your lockfile, commit it to version control, as [uv](https://docs.astral.sh/uv/guides/projects/#uvlock) and [Poetry](https://python-poetry.org/docs/basic-usage/#committing-your-poetrylock-file-to-version-control) both recommend, so every machine installs the same versions.
+3
View File
@@ -9,6 +9,7 @@ How to choose:
- Testing across Python versions: tox, or Nox to configure it in Python
- WebDriver tests on remote machines: Selenium, or SeleniumBase with waits and reports built in
- Load tests written as Python code: Locust
- Timing a function and catching slowdowns: pytest-benchmark
- API tests generated from an OpenAPI or GraphQL schema: Schemathesis
- Replacing objects in a test: unittest.mock, or time-machine for the clock
- Faking HTTP calls: responses for Requests, RESPX for HTTPX, VCR.py to record real traffic
@@ -30,6 +31,8 @@ Selenium drives browsers through WebDriver, a W3C standard, and [Selenium Grid](
A Locust test is [a Python program](https://docs.locust.io/en/stable/quickstart.html): an `HttpUser` class whose tasks make requests, so complex user flows are just code.
pytest-benchmark runs microbenchmarks as pytest tests. A test requests [the `benchmark` fixture](https://pytest-benchmark.readthedocs.io/en/latest/) and calls it with the function to measure and its arguments. The plugin calibrates the runs on its own and returns the function's result, so the test can still assert it's correct. Save a run, then [compare later runs against it](https://pytest-benchmark.readthedocs.io/en/latest/comparing.html), and add `--benchmark-compare-fail` to fail the suite when a test slows down past a threshold you set.
Schemathesis reads your OpenAPI or GraphQL schema and generates property-based tests from it, with no per-endpoint tests to maintain. Its FAQ [recommends the CLI](https://schemathesis.readthedocs.io/en/stable/faq/#how-should-i-run-schemathesis) for most users, with a pytest integration for existing suites.
With unittest.mock, [patch a name where it's looked up](https://docs.python.org/3/library/unittest.mock.html#where-to-patch), which isn't always where it's defined. [Autospec](https://docs.python.org/3/library/unittest.mock.html#autospeccing) your mocks, so a test fails when your code calls an API the real object lacks. For the clock, time-machine [mocks time functions everywhere they're referenced](https://time-machine.readthedocs.io/en/latest/comparison.html), where a patch only reaches the import location it targets.
+7 -2
View File
@@ -1,13 +1,18 @@
Sanitize HTML your users write with nh3, then add a Python web security library for response headers. secure sets CSP and HSTS once for the whole app.
A Python web security library like nh3 sanitizes HTML your users write. Flask-CORS and django-cors-headers add CORS headers to Flask and Django apps.
How to choose:
- User-written HTML, like comments and posts, or code moving off bleach: nh3
- HTML rendered from user Markdown: nh3, run on the rendered output
- Letting a frontend on another origin call your API: Flask-CORS on Flask, django-cors-headers on Django
- Security headers like CSP and HSTS, one policy across ASGI and WSGI apps: secure
nh3 is a Python binding to ammonia, an allowlist-based HTML sanitizer built to stop cross-site scripting from untrusted user HTML. It parses fragments the same way browsers do. Call [`nh3.clean()` on each fragment](https://nh3.readthedocs.io/en/latest/#usage), and pass `tags` and `attributes` to set what it allows, like `tags={"b"}`. When you use the same options everywhere, create an [`nh3.Cleaner`](https://nh3.readthedocs.io/en/latest/#nh3.Cleaner) once and reuse it. ammonia won't linkify bare URLs or add line breaks, so its docs say to [run a markup processor before the sanitizer](https://github.com/rust-ammonia/ammonia#html-sanitization): render Markdown first, then clean the HTML it outputs.
Flask-CORS is a Flask extension built on one idea: when you enable CORS, you enable it for every use case on a domain. [`CORS(app)`](https://flask-cors.corydolphin.com/#getting-started) turns it on for all routes and all origins. To scope it to part of the app, pass [`resources`](https://github.com/corydolphin/flask-cors#resource-specific-cors) to map path patterns like `r"/api/*"` to their own options, such as `origins`. To cover a single route, decorate it with [`@cross_origin()`](https://flask-cors.corydolphin.com/decorator/). Cross-origin cookies stay off until you set `supports_credentials=True`, and its docs ask you to [add CSRF protection before you do](https://flask-cors.corydolphin.com/#using-cors-with-cookies).
django-cors-headers is a Django app that adds CORS headers to responses, so browsers let pages on other origins call your site. Add `corsheaders` to `INSTALLED_APPS`, and put `CorsMiddleware` in `MIDDLEWARE` [as high as possible](https://github.com/adamchainz/django-cors-headers#setup), above any middleware that can return a response, like Django's `CommonMiddleware`. Otherwise those responses go out without CORS headers. Then list the sites you trust in [`CORS_ALLOWED_ORIGINS`](https://github.com/adamchainz/django-cors-headers#cors_allowed_origins-sequencestr). Its docs warn that `CORS_ALLOW_ALL_ORIGINS = True` [can be dangerous](https://github.com/adamchainz/django-cors-headers#cors_allow_all_origins-bool), since any website could then make cross-origin requests to yours.
secure keeps all your security headers in one policy object, instead of header strings copied across handlers and hooks. Start from `Secure.with_default_headers()`, which its docs call [the recommended starting point](https://github.com/TypeError/secure/blob/main/docs/usage.md#start-with-a-preset), configure it once, and reuse it everywhere. [Prefer its ASGI or WSGI middleware](https://github.com/TypeError/secure/blob/main/docs/frameworks.md#how-to-choose-an-integration-style) to cover the whole app, and set headers per response when you're inside an existing hook or view. The defaults are only a starting point: [adjust the Content Security Policy](https://github.com/TypeError/secure#why-use-secure) for the scripts, styles, and third-party services your app really uses. Then [test a stricter policy against the real app](https://github.com/TypeError/secure/blob/main/docs/usage.md#build-an-explicit-configuration) before rollout.
Use both, with sanitizing as the main defense. OWASP says to [sanitize HTML when users author it](https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html#html-sanitization), and to treat CSP as [an additional layer of defense](https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html#other-controls), never the primary one.
Against cross-site scripting, use both a sanitizer and a CSP, with sanitizing as the main defense. OWASP says to [sanitize HTML when users author it](https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html#html-sanitization), and to treat CSP as [an additional layer of defense](https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html#other-controls), never the primary one.