11 · Python Development with AI

Python is the native language of the AI ecosystem. Nearly every model SDK, framework and research codebase ships Python first. Models are also very fluent in it. This page shows how to get high-quality Python from AI and avoid its typical traps.

Level 145 minTracks 5, 10Last verified 2026-10

By the end of this page you can

Why Python matters for AI work, even for Scala developers

A modern toolchain (fast feedback for agents)

ToolPurposeWhy it helps with AI
uvVery fast package/project manager, virtual envs, Python versionsReproducible envs. Agents can uv run anything without activation issues.
ruffLinter + formatter (very fast)Catches AI bugs (unused vars, bare except) in milliseconds. A great post-edit hook.
mypy / pyrightStatic type checkingGives Python some of the "compiler as reviewer" benefit Scala has.
pytest + hypothesisTests + property-based testsVerification that agents can run themselves.
Jupyter / VS Code notebooksInteractive explorationAI assistants work inside notebooks for data exploration.
VS Code Python + Pylance, or PyCharmIDE supportType info and diagnostics feed the AI's context.
uv init pricing-lib && cd pricing-lib
uv add --dev pytest hypothesis ruff mypy
uv run pytest -q
uv run ruff check . --fix && uv run ruff format .
uv run mypy src

Put these exact commands in AGENTS.md (see the Python example on page 07).

Prompting for good Python

Python 3.12, typed (mypy --strict), no external deps.
Write `parse_duration(s: str) -> datetime.timedelta` accepting "90s", "15m", "2h", "1h30m", "1d2h".
Raise ValueError with a helpful message on bad input. Then pytest tests using parametrize,
including invalid inputs, and one hypothesis property (round-trip with a formatter you also write).

Common AI mistakes in Python (watch list)

MistakeWhy it's badAsk for instead
Mutable default args def f(x=[])Shared state between callsx: list | None = None
Bare except: or except Exception: passSwallows real errorsCatch specific exceptions; log or re-raise
float for moneyRounding errorsdecimal.Decimal
Outdated library APIspydantic v1 vs v2, old SDK call styles, pandas deprecationsState versions; paste docs; run tests
SQL via f-stringsSQL injectionParameterised queries
eval, pickle on untrusted data, shell=TrueCode execution vulnerabilitiesSafe parsers; subprocess.run([...])
HTTP calls without timeoutsHangs foreverhttpx.get(url, timeout=10)
Blocking calls inside async defFreezes the event loopAsync clients or asyncio.to_thread
pandas row loops / chained assignmentSlow; SettingWithCopy bugsVectorised ops; .loc
Hallucinated packagesA package name that doesn't exist, or worse, a malicious look-alike ("slopsquatting")Verify on PyPI before installing
Package hallucination is a real security risk Attackers register package names that AI models commonly invent. Before pip install / uv add of an unfamiliar package, check that it exists, is maintained, and is the one you meant. The same applies to Maven/sbt coordinates.

Tests as the safety net

import re
import pytest
from hypothesis import given, strategies as st

def slugify(title: str) -> str:
    """Lowercase, keep a-z0-9, collapse everything else into single hyphens."""
    return re.sub(r"[^a-z0-9]+", "-", title.lower()).strip("-")

@pytest.mark.parametrize("raw, expected", [
    ("Hello World", "hello-world"),
    ("  Scala 3 & Python!  ", "scala-3-python"),
    ("---", ""),
    ("", ""),
])
def test_examples(raw: str, expected: str) -> None:
    assert slugify(raw) == expected

@given(st.text())
def test_properties(raw: str) -> None:
    s = slugify(raw)
    assert s == slugify(s)                      # idempotent
    assert not s.startswith("-") and not s.endswith("-")
    assert "--" not in s
    assert re.fullmatch(r"[a-z0-9-]*", s)
uv run pytest -q test_slugify.py

Hypothesis generates hundreds of random inputs, including Unicode, and checks the properties. This kind of test catches the edge cases AI-written code most often misses.

Your first LLM call from Python

Standard library only, against a local OpenAI-compatible server (Ollama). For hosted providers, use their official SDK and an API key from an environment variable.

import json
import os
import sys
import urllib.request

ENDPOINT = os.environ.get("LLM_ENDPOINT", "http://localhost:11434/v1/chat/completions")
MODEL = os.environ.get("LLM_MODEL", "llama3.2")


def ask(question: str) -> str:
    payload = {
        "model": MODEL,
        "temperature": 0.2,
        "messages": [
            {"role": "system", "content": "You are a concise Python expert."},
            {"role": "user", "content": question},
        ],
    }
    headers = {"Content-Type": "application/json"}
    if key := os.environ.get("LLM_API_KEY"):          # hosted providers
        headers["Authorization"] = f"Bearer {key}"
    req = urllib.request.Request(ENDPOINT, data=json.dumps(payload).encode(), headers=headers)
    with urllib.request.urlopen(req, timeout=120) as resp:
        data = json.load(resp)
    return data["choices"][0]["message"]["content"]


if __name__ == "__main__":
    print(ask(" ".join(sys.argv[1:]) or "Explain Python decorators in 3 sentences."))

Compare it with the Scala version on page 10. The request shape is identical. That is the value of a common API shape.

Everyday Python + AI wins

Scala conceptClosest Python equivalent
case class@dataclass(frozen=True), or pydantic models
enum / sealed trait ADTenum.Enum, or a union of dataclasses + match
Option[A]A | None (checked by mypy)
Either[E, A]Exceptions (idiomatic), or a Result type library
for-comprehensionlist/generator comprehensions
LazyList / Iteratorgenerators (yield)
Type classes / givenProtocols (typing.Protocol), functools.singledispatch
Cats Effect IOasyncio coroutines (different model)
Try it (25 min)
  1. Create a project with uv init, add pytest/hypothesis/ruff/mypy.
  2. Use the parse_duration prompt above with your IDE agent and tell it to run tests until they pass.
  3. Run ruff and mypy --strict. How many issues did the AI leave? Ask it to fix them.
  4. Run ask_llm.py against a local model (install Ollama) or a hosted one.

Key takeaways