Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"nativeint",
"ncave",
"pathlib",
"pyfile",
"pyname",
"Pyxpecto",
"stroustrup",
Expand Down
23 changes: 13 additions & 10 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,16 +58,19 @@ Use `` F`#` `` (backticks around `#`) in markdown headings to prevent markdownli

```text
chapters/
├── introduction.fs # What is Fable.Python, why use it
├── python.fs # F# concepts for Python developers
├── getting-started.fs # Setup, first project, hello world
├── interop.fs # Using existing Python libraries
├── bindings.fs # Creating Python bindings
├── compatibility.fs # F# features supported, limitations
├── async-programming.fs # async vs task, Python asyncio mapping
├── fable-v5.fs # Fable v5 features, Rust core, PyPI
├── pydantic.fs # Pydantic models, DTOs, validation
└── units-of-measure.fs # Compile-time dimensional analysis
├── Introduction.fs # What is Fable.Python, why use it
├── Python.fs # F# concepts for Python developers
├── GettingStarted.fs # Setup, first project, hello world
├── Interop.fs # Using existing Python libraries
├── Bindings.fs # Creating Python bindings
├── Compatibility.fs # F# features supported, limitations
├── AsyncProgramming.fs # async vs task, Python asyncio mapping
├── Testing.fs # Testing F# code with Python test runners
├── FableV5.fs # Fable v5 features, Rust core, PyPI
├── Pydantic.fs # Pydantic models, DTOs, validation
├── UnitsOfMeasure.fs # Compile-time dimensional analysis
├── FableLiterate.fs # Symlink → ../Fable.Literate/App.fs
└── Summary.fs # Wrap-up, resources, repo link
Fable.Literate/
├── App.fs # Fable.Literate converter source (F#)
└── Fable.Literate.fsproj
Expand Down
13 changes: 2 additions & 11 deletions Fable.Literate/App.fs
Original file line number Diff line number Diff line change
Expand Up @@ -464,15 +464,6 @@ dotnet fable Fable.Literate/ --lang python -o output/Fable.Literate/
python output/Fable.Literate/app.py chapters/introduction.fs > docs/introduction.md
```

That's it! A complete literate programming converter in under 200 lines of F#.

## The Punchline

If you're reading this, the code worked.

This entire blog post - every chapter, every code example, every explanation -
was processed by the F# code you just read, compiled to Python, and output
as Markdown. The proof is in the reading.

Welcome to Fable.Python. Now go build something.
That's it! A complete literate programming converter in under 200 lines of F`#`,
compiled to Python, processing this very blog post.
*)
11 changes: 7 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@ This is a comprehensive guide to [Fable.Python](https://github.com/fable-compile
8. **Libraries** - Existing ecosystem (Thoth.Json, AsyncRx, Siren, etc.) *(coming soon)*
9. **Pydantic** - Pydantic interop with Decorate and ClassAttributes
10. **Units of Measure** - Compile-time dimensional analysis
11. **Testing** - XUnit and Fable.Pyxpecto *(coming soon)*
12. **Fabletext: The Strange Loop** - The self-documenting finale
11. **Testing** - Testing F# code with Python test runners
12. **Fable.Literate** - The self-documenting converter
13. **Summary** - Wrap-up, resources, and contributing

## The Strange Loop

Expand Down Expand Up @@ -74,7 +75,9 @@ chapters/
├── Testing.fs # Testing with Python
├── FableV5.fs # What's new in Fable v5
├── Pydantic.fs # Pydantic interop
└── UnitsOfMeasure.fs # Dimensional analysis
├── UnitsOfMeasure.fs # Dimensional analysis
├── FableLiterate.fs # Symlink → ../Fable.Literate/App.fs
└── Summary.fs # Wrap-up and resources
Fable.Literate/
├── App.fs # Fable.Literate converter (F#)
└── Fable.Literate.fsproj
Expand All @@ -94,7 +97,7 @@ docs/
Defined in `justfile`:

```just
chapters := "Introduction Python GettingStarted Interop Bindings Compatibility AsyncProgramming Testing FableV5 Pydantic UnitsOfMeasure"
chapters := "Introduction Python GettingStarted Interop Bindings Compatibility AsyncProgramming Testing FableV5 Pydantic UnitsOfMeasure FableLiterate Summary"
```

To add a new chapter, just add the file and update this list.
Expand Down
1 change: 1 addition & 0 deletions chapters/FableLiterate.fs
54 changes: 54 additions & 0 deletions chapters/Summary.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
(*** hide ***)
module Summary

(**
# Summary

We've covered a lot of ground in this guide:

- **Introduction**: What Fable.Python is and why it matters
- **F`#` for Python Developers**: Bridging the conceptual gap between languages
- **Getting Started**: Setting up your first Fable.Python project
- **Interop**: Seamlessly calling Python libraries from F`#`
- **Bindings**: Creating type-safe wrappers for Python code
- **Compatibility**: Understanding what F`#` features work (and which don't)
- **Async Programming**: Mapping F`#` async to Python's asyncio
- **Testing**: Running F`#` code with pytest and other Python test runners
- **Fable v5**: The latest features including the Rust core and PyPI packages
- **Pydantic**: Building validated data models with Python's favorite library
- **Units of Measure**: Compile-time dimensional analysis that vanishes at runtime
- **Fable.Literate**: A self-documenting literate programming converter

## The Punchline

If you're reading this, the code worked.

This entire blog post - every chapter, every code example, every explanation -
was processed by F`#` code compiled to Python, and output as Markdown.
The proof is in the reading.

## Get Involved

The source code for this entire project is available on GitHub:

**[github.com/cardamomcode/fable-python](https://github.com/cardamomcode/fable-python)**

The repository contains:
- All the chapter source files (literate F`#`)
- The Fable.Literate converter
- Build scripts and configuration
- The generated blog post

Found a typo? Want to improve an explanation? Have a better example?
Pull requests are welcome! This is a living document, and contributions
from the community make it better for everyone.

## Resources

- [Fable Documentation](https://fable.io/docs/)
- [Fable.Python on GitHub](https://github.com/fable-compiler/Fable.Python/)
- [F`#` Software Foundation](https://fsharp.org/)
- [Fable Discord](https://discord.gg/8c3Ng9N) - The Fable community is friendly and helpful

Welcome to Fable.Python. Now go build something.
*)
1 change: 1 addition & 0 deletions fable-python.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
<Compile Include="chapters/FableV5.fs" />
<Compile Include="chapters/Pydantic.fs" />
<Compile Include="chapters/UnitsOfMeasure.fs" />
<Compile Include="chapters/Summary.fs" />
</ItemGroup>

</Project>
30 changes: 16 additions & 14 deletions justfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

# Chapter order for documentation generation
# Edit this list to reorder or add chapters
chapters := "Introduction Python GettingStarted Interop Bindings Compatibility AsyncProgramming Testing FableV5 Pydantic UnitsOfMeasure"
chapters := "Introduction Python GettingStarted Interop Bindings Compatibility AsyncProgramming Testing FableV5 Pydantic UnitsOfMeasure FableLiterate Summary"

# Default: show help
default:
Expand Down Expand Up @@ -38,16 +38,17 @@ generate: build format-python
for name in {{chapters}}; do
# Convert PascalCase to snake_case for Python file naming
pyname=$(echo "$name" | sed 's/\([A-Z]\)/_\1/g' | sed 's/^_//' | tr '[:upper:]' '[:lower:]')
# FableLiterate uses different python output path (symlink to Fable.Literate/App.fs)
if [ "$name" = "FableLiterate" ]; then
pyfile="output/Fable.Literate/python.py"
else
pyfile="output/chapters/chapters/${pyname}.py"
fi
uv run python output/Fable.Literate/app.py \
--python-file "output/chapters/chapters/${pyname}.py" \
--python-file "$pyfile" \
"chapters/${name}.fs" > "docs/${name}.md"
echo "Generated docs/${name}.md"
done
# Also generate Fable.Literate documentation
uv run python output/Fable.Literate/app.py \
--python-file "output/Fable.Literate/python.py" \
Fable.Literate/App.fs > docs/fable-literate.md
echo "Generated docs/fable-literate.md"
# Fix markdown lint issues
just lint-markdown

Expand All @@ -59,25 +60,26 @@ blogpost: build format-python
for name in {{chapters}}; do
# Convert PascalCase to snake_case for Python file naming
pyname=$(echo "$name" | sed 's/\([A-Z]\)/_\1/g' | sed 's/^_//' | tr '[:upper:]' '[:lower:]')
# FableLiterate uses different python output path (symlink to Fable.Literate/App.fs)
if [ "$name" = "FableLiterate" ]; then
pyfile="output/Fable.Literate/python.py"
else
pyfile="output/chapters/chapters/${pyname}.py"
fi
if $first; then
# First chapter keeps original header levels (has the title)
uv run python output/Fable.Literate/app.py \
--python-file "output/chapters/chapters/${pyname}.py" \
--python-file "$pyfile" \
"chapters/${name}.fs" > docs/blogpost.md
first=false
else
# Remaining chapters get headers increased by one level
echo "" >> docs/blogpost.md
uv run python output/Fable.Literate/app.py \
--python-file "output/chapters/chapters/${pyname}.py" \
--python-file "$pyfile" \
--increase-headers "chapters/${name}.fs" >> docs/blogpost.md
fi
done
# Include Fable.Literate documenting itself (the meta twist!)
echo "" >> docs/blogpost.md
uv run python output/Fable.Literate/app.py \
--python-file "output/Fable.Literate/python.py" \
--increase-headers Fable.Literate/App.fs >> docs/blogpost.md
echo "Generated docs/blogpost.md"
# Fix markdown lint issues
just lint-markdown
Expand Down