# Python Devcontainer

> ALWAYS load alongside the python-project skill for any new or existing Python project. Creates a Dev Container configuration for Python projects using uv. Ask the user if they want devcontainers set up during project scaffolding.

- Skill: `hugobatista/python-devcontainer` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add hugobatista/python-devcontainer`
- Raw SKILL.md: https://api.skillmd.com/api/skills/hugobatista/python-devcontainer/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: hugobatista (https://skillmd.com/u/hugobatista)
- Updated: 2026-09-22
- Page: https://skillmd.com/skills/hugobatista/python-devcontainer

---


## Dev Container setup

When scaffolding a new Python project, or when adding Dev Containers to an existing one:

### Step 1: Ask the user

Ask: *"Do you want to set up a Dev Container for this project?"*

If no, skip.

If yes, proceed with Step 2.

### Step 2: Gather project metadata

Read the project's `pyproject.toml`:

| Info | Source |
|------|--------|
| Python version | `requires-python` field — extract the minor version (e.g. `>=3.12` → `3.12`) |
| uv version | `[tool.uv] required-version` — extract the bare version (e.g. `"==0.11.18"` → `0.11.18`) |
| Sync command | If `[dependency-groups]` has a `dev` group, use `uv sync --group dev`; otherwise use `uv sync` |

Then extract the uv version from `[tool.uv] required-version`:

```bash
uv_version=$(grep "^required-version" pyproject.toml \
  | sed 's/.*"\([0-9.]*\)".*/\1/')
```

This strips any leading operator (`==`, `>=`, `~=`) and returns the bare version (e.g. `0.11.18`).

Then fetch the image digest for the selected Python version:

```bash
curl -s "https://mcr.microsoft.com/v2/devcontainers/python/manifests/2-{python_version}-trixie" \
  -H "Accept: application/vnd.oci.image.index.v1+json" \
  -H "Accept: application/vnd.docker.distribution.manifest.list.v2+json" \
  -o /dev/null -w "%{header_json}" 2>/dev/null \
| python3 -c "import json,sys; print(json.load(sys.stdin).get('docker-content-digest',['N/A'])[0])"
```

This returns the manifest list digest (e.g. `sha256:132c3dc2b618d194d5d56e4b385e11f5f67b265008a3d7cdddf806ab3ff8572b`).

### Step 3: Substitute and write

Read `assets/devcontainer.json.tmpl` and substitute placeholders:

| Placeholder | Value |
|---|---|---|
| `{{python_version}}` | The minor version from step 2 (e.g. `3.12`) |
| `{{python_image_digest}}` | The SHA256 digest from step 2 (e.g. `sha256:132c3dc2b618d194d5d56e4b385e11f5f67b265008a3d7cdddf806ab3ff8572b`) |
| `{{uv_version}}` | The bare uv version from step 2 (e.g. `0.11.18`) |
| `{{post_create_command}}` | The sync command from step 2 (e.g. `uv sync --group dev`) |

Write the result to `.devcontainer/devcontainer.json`.

Create the directory before writing:
```bash
mkdir -p .devcontainer
```

The `devcontainer-lock.json` is auto-generated by VS Code on first build — do not create it manually.

> **SELinux (Fedora/RHEL)**: On systems with SELinux enforcing, Docker bind-mounts of files with `user_home_t` context cause `Permission denied` inside the container. The `--security-opt label=disable` flag in `runArgs` disables SELinux separation for the container. It is a no-op on macOS and Windows.

> **Cache invalidation**: Feature options in devcontainer.json are passed as env vars to the install script, but Docker's build cache only tracks files — not env vars. After changing feature options, use **Dev Containers: Rebuild Container Without Cache** in VS Code.

### Step 4: README badge

After creating the Dev Container, add the following badge to `README.md`:

```
[![Dev Container](https://img.shields.io/badge/Dev--Container-ready-brightgreen?logo=visualstudiocode)](https://containers.dev)
```

