惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Application and Cybersecurity Blog
Application and Cybersecurity Blog
S
Securelist
K
Kaspersky official blog
Scott Helme
Scott Helme
C
CXSECURITY Database RSS Feed - CXSecurity.com
GbyAI
GbyAI
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
C
Cisco Blogs
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
博客园 - Franky
Security Latest
Security Latest
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Y
Y Combinator Blog
T
Threat Research - Cisco Blogs
L
LINUX DO - 热门话题
C
Cyber Attacks, Cyber Crime and Cyber Security
Project Zero
Project Zero
Cisco Talos Blog
Cisco Talos Blog
月光博客
月光博客
I
Intezer
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
人人都是产品经理
人人都是产品经理
L
Lohrmann on Cybersecurity
Recorded Future
Recorded Future
Latest news
Latest news
V2EX - 技术
V2EX - 技术
T
The Exploit Database - CXSecurity.com
H
Heimdal Security Blog
F
Fortinet All Blogs
Cloudbric
Cloudbric
IT之家
IT之家
博客园 - 叶小钗
Microsoft Security Blog
Microsoft Security Blog
P
Proofpoint News Feed
博客园 - 司徒正美
Apple Machine Learning Research
Apple Machine Learning Research
PCI Perspectives
PCI Perspectives
AWS News Blog
AWS News Blog
H
Help Net Security
S
Security @ Cisco Blogs
酷 壳 – CoolShell
酷 壳 – CoolShell
Recent Announcements
Recent Announcements
Hacker News - Newest:
Hacker News - Newest: "LLM"
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
F
Full Disclosure
S
Schneier on Security
S
Security Affairs
T
Tenable Blog

alexwlchan’s notes

Managing the caption of a photo with AppleScript (but not PhotoKit) Goodhart’s and Campbell’s Law are different Notes from The Cornishman No. 176 (Spring 2026) Notes from The Cornishman No. 176 (Spring 2026) GitUp can’t diff text files larger than 8MB Home Testing the width of a page on a mobile device using Playwright Disable AirPods charging notifications Filter a list of JSON object based on a list of tags HOME_GET_ME_HOME is a Citymapper Shortcuts action The FileExistsError exception exposes a filename attribute The red-lined bubble snail Why can’t Python connect to example.com? Useful type hints for Python How to truncate the middle of long command output AirPlay Receiver can interfere with Flask apps What’s the main prefix in SQLite queries? The file(1) command can read SQLite databases My randline project is tested by Crater Drawing an image with Liquid Glass using SwiftUI Previews Road signs in the Soviet union don’t have circular heads Setting up golink in my personal tailnet Create a file atomically in Go Get a map of IP addresses for devices in my tailnet The SQLite command line shell will count your unclosed parentheses Use SQL triggers to prevent overwriting a value Testing date formatting with date-fns-tz and different timezones The “strangler” pattern is named after a tree, not an act of violence Place with the same name, but different etymology
Start a Caddy server in a subprocess during a Python session
2026-04-30 · via alexwlchan’s notes

Start the server with subprocess.Popen, poll until it’s available, yield the base URL, then clean up the process when you’re done.

For local developemnt and testing of this site, I’ve started to run a Caddy server with my real config to be a better replica of my real setup. I wanted to start a Caddy server in my automated tests, and shut it down when I’m done.

Here’s the fixture I came up with:

from collections.abc import Iterator
import subprocess
from subprocess import PIPE
import time
import urllib.error
import urllib.request

import pytest


@pytest.fixture(scope="session")
def caddy_server_url() -> Iterator[str]:
    """
    Start an instance of Caddy running in the current directory, and
    return the base URL.
    """
    port = 5858
    cmd = ["caddy", "file-server", "--listen", f":{port}"]

    with subprocess.Popen(cmd, stdout=PIPE, stderr=PIPE) as proc:
        url = f"http://localhost:{port}/"

        # Wait for up to a second waiting for the server to start.
        #
        # If we get a ConnectionRefusedError, the server hasn't started yet.
        # If we get an HTTPError or a 200 OK, we've connected to the server
        # and it's serving HTTP traffic, so it's started.
        t0 = time.time()
        while time.time() - t0 < 1:
            try:
                urllib.request.urlopen(url)
            except urllib.error.HTTPError:
                break
            except urllib.error.URLError as exc:
                if exc.args and isinstance(exc.args[0], ConnectionRefusedError):
                    pass
                else:
                    raise
            else:
                break

        assert not proc.poll()

        yield url

        proc.terminate()
        proc.wait(timeout=1)

And here’s what a test looks like:

def test_can_start_web_server(caddy_server_url: str) -> None:
    """
    Fetch a page from the running web server.
    """
    resp = urllib.request.urlopen(caddy_server_url + "example.py")
    assert b"test_can_start_web_server" in resp.read()

The fixture starts a file server with subprocess, then polls until the server is available. On my Mac mini, Caddy takes ~0.01s to start – long enough I can’t start running tests immediately, fast enough that any fixed sleep would be inefficient (especially as it’ll be slower in CI).

At the end of the fixture, I call proc.terminate() and proc.wait() to clean everything up. The terminate() sends a SIGTERM; the wait() blocks until the child process terminates. The process shuts down quickly, but I do need to wait or I get warnings from pytest that I have an unterminated process.

The fixture is session-scoped so I only have to start/stop the server once across my test suite.

In my real codebase, this code is split across two functions – a function that starts the server, and a function that wraps it in a pytest fixture. I reuse the server function in my serve_site.py script, which runs a local development server for the site. I’m also pointing it at a Caddyfile with my site config, rather than running a bare file_server.

This is similar to Simon Willison’s recipe.