Skip to content

Commit f0d1fce

Browse files
authored
Merge pull request #5 from cardamomcode/pytdantic-update
docs: Pydantic update
2 parents 16aeeb0 + bf3c85b commit f0d1fce

12 files changed

Lines changed: 685 additions & 203 deletions

.vscode/settings.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
11
{
22
"cSpell.words": [
3+
"coro",
34
"destructures",
45
"elif",
56
"eprintln",
67
"fabletext",
78
"Fantomas",
89
"fastapi",
910
"Feliz",
11+
"getcwd",
1012
"Hashnode",
13+
"pathlib",
1114
"pyname",
1215
"stroustrup"
1316
]

CLAUDE.md

Lines changed: 54 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
66

77
**F# Advent 2025 blog post project** demonstrating Fable.Python capabilities.
88

9-
**Fabletext** is a literate programming converter (inspired by jupytext) written in F# that transpiles to Python via Fable.Python. It processes `.fs` files with embedded Markdown comments (FSharp.Formatting conventions) and outputs GitHub-flavored Markdown suitable for publishing on platforms like Hashnode.
9+
**Fable.Literate** is a literate programming converter (inspired by jupytext) written in F# that transpiles to Python via Fable.Python. It processes `.fs` files with embedded Markdown comments (FSharp.Formatting conventions) and outputs GitHub-flavored Markdown suitable for publishing on platforms like Hashnode.
1010

1111
**Key concept:** The project is self-documenting - the chapters and converter generate the blog post that documents how they work.
1212

@@ -19,57 +19,86 @@ just build # Build all chapters and tools to Python
1919
just generate # Generate individual markdown docs from chapters
2020
just blogpost # Generate concatenated blogpost.md for publishing
2121
just format # Format Python with ruff
22-
just lint # Lint Python (ruff) and Markdown (markdownlint)
22+
just lint # Lint Markdown (markdownlint)
2323
just watch # Watch mode for development
2424
just clean # Clean generated files
2525
just all # Full pipeline: restore, build, generate, format, lint
2626
```
2727

2828
## Architecture
2929

30-
### Fabletext Parser State Machine
30+
### Fable.Literate AST Pipeline
3131

32-
The converter uses a line-by-line state machine with three states:
32+
The converter follows a compiler-like architecture with three phases:
3333

34-
- **InMarkdown**: Inside `(** ... *)` blocks - emit content as-is
35-
- **InCode**: F# code outside comment blocks - wrap in fenced code blocks
36-
- **Hidden**: After `(*** hide ***)` - skip until next markdown block
34+
1. **Parse**: Convert source lines into a Block AST
35+
2. **Transform**: Filter hidden blocks, resolve Python includes
36+
3. **Print**: Render the AST as Markdown
3737

38-
### Input/Output Transformation
38+
### Literate Directives
3939

40-
| Input Pattern | Output |
41-
|--------------|--------|
40+
| Directive | Purpose |
41+
|-----------|---------|
4242
| `(** content *)` | Raw markdown content |
43-
| `(*** hide ***)` | Nothing (enters hidden mode) |
43+
| `(*** hide ***)` | Hide following code from output |
44+
| `(*** include-python: symbol ***)` | Include generated Python for symbol |
4445
| Regular F# code | Wrapped in ```fsharp fenced blocks |
4546

47+
### Escaping F# in Headings
48+
49+
Use `` F`#` `` (backticks around `#`) in markdown headings to prevent markdownlint from interpreting it as ATX closed style:
50+
51+
```fsharp
52+
(**
53+
## F`#` Async Workflows
54+
*)
55+
```
56+
4657
### File Structure
4758

4859
```text
4960
chapters/
50-
├── 01-introduction.fs # What is Fable.Python, why use it
51-
├── 02-getting-started.fs # Setup, first project, hello world
52-
├── 03-bindings.fs # Python interop, type bindings
53-
└── 04-compatibility.fs # F# features supported, limitations
54-
tools/
55-
├── fabletext.fs # Fabletext converter source (F#)
56-
└── fabletext.fsproj
61+
├── introduction.fs # What is Fable.Python, why use it
62+
├── python.fs # F# concepts for Python developers
63+
├── getting-started.fs # Setup, first project, hello world
64+
├── interop.fs # Using existing Python libraries
65+
├── bindings.fs # Creating Python bindings
66+
├── compatibility.fs # F# features supported, limitations
67+
├── async-programming.fs # async vs task, Python asyncio mapping
68+
├── fable-v5.fs # Fable v5 features, Rust core, PyPI
69+
├── pydantic.fs # Pydantic models, DTOs, validation
70+
└── units-of-measure.fs # Compile-time dimensional analysis
71+
Fable.Literate/
72+
├── App.fs # Fable.Literate converter source (F#)
73+
└── Fable.Literate.fsproj
5774
output/
58-
├── chapters/ # Generated Python from chapters
59-
└── tools/
60-
└── fabletext.py # Generated converter (Python)
75+
├── chapters/ # Generated Python from chapters
76+
└── Fable.Literate/
77+
└── app.py # Generated converter (Python)
6178
docs/
62-
├── *.md # Individual chapter markdown
63-
└── blogpost.md # Concatenated for Hashnode
79+
├── *.md # Individual chapter markdown
80+
└── blogpost.md # Concatenated for Hashnode
6481
```
6582

83+
## Chapter Writing Guidelines
84+
85+
- Each chapter is a literate F# file with embedded markdown
86+
- Use `(** ... *)` for markdown content
87+
- Use `(*** hide ***)` to hide setup code (module declarations, imports)
88+
- Use `(*** include-python: symbolName ***)` to show generated Python
89+
- Tables are auto-formatted by markdownlint - don't fight it
90+
- Keep code examples self-contained and buildable
91+
6692
## Fable.Python Considerations
6793

68-
- Use `Fable.Core` attributes where needed
94+
- Use `Fable.Core` attributes (`[<Emit>]`, `[<Import>]`, etc.)
95+
- Use `Fable.Python.Pydantic` for Pydantic interop
6996
- Stick to Fable-compatible F# subset
70-
- File I/O via Python interop (`[<Emit>]` with `open`, `read`, etc.)
97+
- `task { }` compiles to native Python `async def` (Fable v5)
98+
- `async { }` for multi-target code (Python, .NET, JS)
7199

72100
## Resources
73101

74102
- [Fable.Python docs](https://fable.io/docs/getting-started/python.html)
75103
- [Fable.Python GitHub](https://github.com/fable-compiler/Fable.Python/)
104+
- [Content Plan](CONTENT-PLAN.md) - Chapter structure and TODO items

chapters/async-programming.fs

Lines changed: 37 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -126,24 +126,13 @@ The `task` computation expression in .NET creates *hot* tasks that start immedia
126126
However, when compiled to Python via Fable, tasks become Python coroutines - which are
127127
*cold* just like Python's native `async def` functions.
128128
129-
In Fable v5, tasks compile to Python's native `async def` syntax, enabling seamless
130-
integration with frameworks like FastAPI.
129+
A key improvement in Fable v5 is that `task { }` now compiles to Python's native
130+
`async def` syntax. Previously, Fable generated regular functions returning `Awaitable[T]`,
131+
which frameworks like FastAPI couldn't recognize as async endpoints.
131132
*)
132133

133134
open System.Threading.Tasks
134135

135-
let fetchDataTask () =
136-
task {
137-
do! Task.Delay 1000
138-
return "data from task"
139-
}
140-
141-
(**
142-
### Fable v5: Native Python Async
143-
144-
A key improvement in Fable v5 is that `task { }` now compiles to Python's `async def`:
145-
*)
146-
147136
let processItemTask (item: string) =
148137
task {
149138
do! Task.Delay 100
@@ -152,15 +141,11 @@ let processItemTask (item: string) =
152141

153142
(**
154143
This generates:
144+
*)
145+
(*** include-python: processItemTask ***)
155146

156-
```python
157-
async def process_item_task(item: str) -> str:
158-
await asyncio.sleep(0.1)
159-
return item.upper()
160-
```
161-
162-
Previously, Fable generated regular functions returning `Awaitable[T]`, which frameworks
163-
like FastAPI couldn't recognize as async endpoints. Now the integration is seamless.
147+
(**
148+
Now frameworks like FastAPI can detect and handle these as proper async endpoints.
164149
165150
### Task vs Async: Key Differences
166151
@@ -188,6 +173,12 @@ like FastAPI couldn't recognize as async endpoints. Now the integration is seaml
188173
### Working with Tasks
189174
*)
190175

176+
let fetchDataTask () =
177+
task {
178+
do! Task.Delay 100 // Do some async work
179+
return "data from task"
180+
}
181+
191182
let taskExample () =
192183
task {
193184
let! result = fetchDataTask ()
@@ -243,6 +234,30 @@ In Python, this generates:
243234

244235
(*** include-python: simpleTask ***)
245236

237+
(**
238+
### Running Tasks from F`#`
239+
240+
To run a task and get its result in F#:
241+
*)
242+
243+
let runTaskExample () =
244+
let tsk = simpleTask ()
245+
246+
// Block and wait for result
247+
let result = tsk.GetAwaiter().GetResult()
248+
printfn $"Got: {result}"
249+
250+
(**
251+
You can also await tasks inside other tasks:
252+
*)
253+
254+
let chainedTasks () =
255+
task {
256+
let! first = simpleTask ()
257+
let! second = simpleTask ()
258+
return first + second
259+
}
260+
246261
(**
247262
### Running in Python's Event Loop
248263
@@ -251,7 +266,6 @@ When your compiled Python code runs, you'll need an event loop. For scripts:
251266
```python
252267
import asyncio
253268
254-
# If using task-based code
255269
async def main():
256270
result = await simple_task()
257271
print(result)

chapters/fable-v5.fs

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,11 @@ module FableV5
4545
- **Fixed-size arrays** - No more Python list quirks for byte streams
4646
- **Reliable numerics** - Fable 4's pure Python numerics were a constant source of bugs
4747
48+
While Rust is fast, don't expect dramatic speedups for typical F# code. Many F#
49+
functions are higher-order and callback to Python - `List.map`, `List.filter`,
50+
`Seq.fold`, etc. all invoke your Python lambdas. The Rust core handles the
51+
data structures correctly; your code still runs at Python speed.
52+
4853
## fable-library via PyPI
4954
5055
Before Fable v5, the runtime was bundled in the NuGet package and copied
@@ -58,6 +63,19 @@ pip install fable-library
5863
uv add fable-library
5964
```
6065
66+
For projects, pin your dependencies in `pyproject.toml`. For stable releases use
67+
a minimum version constraint:
68+
69+
```toml
70+
dependencies = ["fable-library>=5.0.0"]
71+
```
72+
73+
For alpha/beta releases, pin the exact version to avoid surprises:
74+
75+
```toml
76+
dependencies = ["fable-library==5.0.0a21"]
77+
```
78+
6179
This makes dependency management much simpler and follows Python conventions.
6280
6381
## Test Coverage
@@ -78,10 +96,10 @@ To use Fable v5, install the alpha CLI:
7896
7997
```bash
8098
# Install Fable 5 CLI
81-
dotnet tool install fable --version 5.0.0-alpha.17
99+
dotnet tool install fable --version 5.0.0-alpha.21
82100
83101
# Add Fable.Core to your project
84-
dotnet add package Fable.Core --version 5.0.0-beta.2
102+
dotnet add package Fable.Core --version 5.0.0-beta.4
85103
86104
# Install the Python runtime
87105
uv add fable-library==5.0.0a17

chapters/getting-started.fs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ running as Python.
1010
1111
You'll need:
1212
13-
- [.NET SDK](https://dotnet.microsoft.com/download) (6.0 or later)
13+
- [.NET SDK](https://dotnet.microsoft.com/download) (6.0 or later. We recommend
14+
installing the latest LTS version, currently .NET 10
1415
- [Python 3.12+](https://www.python.org/downloads/) (Fable targets Python 3.12 or higher)
1516
1617
## Project Setup

chapters/introduction.fs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@ F# is a functional-first language with powerful features like:
2323
2424
With Fable.Python, you get all these benefits while targeting the Python ecosystem.
2525
26+
Python is the [most popular programming language](https://www.tiobe.com/tiobe-index/)
27+
in the world. And no matter what you think of Python, it will always be the second
28+
best language for everything. That ubiquity is exactly why Fable.Python exists.
29+
2630
## When to Use Fable.Python
2731
2832
Fable.Python is a great choice when:

0 commit comments

Comments
 (0)