diff --git a/README.md b/README.md
index 1759d94cb..dae55f326 100644
--- a/README.md
+++ b/README.md
@@ -16,7 +16,7 @@
[](https://docs.codegen.com)
[](https://community.codegen.com)
[](https://github.com/codegen-sh/codegen-sdk/tree/develop?tab=Apache-2.0-1-ov-file)
-[](https://x.com/codegen)
+[](https://x.com/codegen)
diff --git a/docs/introduction/getting-started.mdx b/docs/introduction/getting-started.mdx
index 7c0463f37..cdb805452 100644
--- a/docs/introduction/getting-started.mdx
+++ b/docs/introduction/getting-started.mdx
@@ -9,7 +9,7 @@ A quick tour of Codegen in a Jupyter notebook.
## Installation
-Install [`codegen` on pypi](https://pypi.org/project/codegen/) via [uv](https://github.com/astral-sh/uv):
+Install [codegen](https://pypi.org/project/codegen/) on Pypi via [uv](https://github.com/astral-sh/uv):
```bash
uv tool install codegen
@@ -34,8 +34,8 @@ The [codgen notebook](/cli/notebook) command creates a virtual environment and o
# Navigate to your repository
cd path/to/git/repository
-# Initialize codegen and launch Jupyter
-codegen notebook
+# Initialize codegen and launch Jupyter with a demo notebook
+codegen notebook --demo
```
This will:
@@ -47,7 +47,7 @@ This will:
2. Launch Jupyter Lab with a pre-configured notebook
- The notebook comes pre-configured to load your codebase, so you can start
+ The `notebook --demo` comes pre-configured to load [FastAPI](https://github.com/fastapi/fastapi)'s codebase , so you can start
exploring right away!
@@ -58,13 +58,17 @@ Instantiating a [Codebase](/api-reference/core/Codebase) will automatically pars
```python
from codegen import Codebase
-# Parse a codebase
-codebase = Codebase("./")
+# Initialize FastAPI codebase
+print('Cloning and parsing FastAPI to /tmp/codegen/fastapi...')
+codebase = Codebase.from_repo('fastapi/fastapi')
+
+# To initialize an existing local codebase, use this constructor
+# codebase = Codebase("path/to/git/repo")
```
This will automatically infer the programming language of the codebase and
- parse all files in the codebase.
+ parse all files in the codebase. Learn more about [parsing codebases here](/building-with-codegen/parsing-codebases)
@@ -81,8 +85,10 @@ Here are some common patterns for code navigation in Codegen:
- Iterate over all [Functions](/api-reference/core/Function) with [Codebase.functions](/api-reference/core/Codebase#functions)
- View class inheritance with [Class.superclasses](/api-reference/core/Class#superclasses)
-- View function call-sites with [Function.call_sites](/api-reference/core/Function#call-sites)
- View function usages with [Function.usages](/api-reference/core/Function#usages)
+- View inheritance hierarchies with [inheritance APIs](https://docs.codegen.com/building-with-codegen/class-api#working-with-inheritance)
+- Identify recursive functions by looking at [FunctionCalls](https://docs.codegen.com/building-with-codegen/function-calls-and-callsites)
+- View function call-sites with [Function.call_sites](/api-reference/core/Function#call-sites)
```python
# Print overall stats
@@ -108,7 +114,7 @@ if recursive:
print(f" - {func.name}")
```
-### Analyzing Tests
+## Analyzing Tests
Let's specifically drill into large test files, which can be cumbersome to manage.
@@ -135,38 +141,52 @@ for file, num_tests in file_test_counts.most_common()[:5]:
print(f" ๐ก Functions: {len(file.functions)}")
```
-### Splitting Up Large Test Files
+## Splitting Up Large Test Files
+
+Lets split up the largest test files into separate modules for better organization.
+
+This uses Codegen's [codebase.move_to_file(...)](/building-with-codegen/moving-symbols), which will:
+- update all imports
+- (optionally) move dependencies
+- do so very fast โก๏ธ
-Lets split up the largest test files into separate modules for better organization:
+While maintaining correctness.
```python
-print("\n๐ฆ Splitting Test Files")
+filename = 'tests/test_path.py'
+print(f"๐ฆ Splitting Test File: {filename}")
print("=" * 50)
-# Process top 5 largest test files
-for file, num_tests in file_test_counts.most_common()[:5]:
- # Create a new directory based on the file name
- base_name = file.path.replace('.py', '')
- print(f"\n๐ Processing: {file.filepath}")
- print(f" ๐ {num_tests} test classes to split")
-
- # Move each test class to its own file
- for test_class in file.classes:
- if test_class.name.startswith('Test'):
- # Create descriptive filename from test class name
- new_file = f"{base_name}/{test_class.name.lower()}.py"
- print(f" ๐ Moving {test_class.name} -> {new_file}")
-
- # Codegen handles all the complexity:
- # - Creates directories if needed
- # - Updates all imports automatically
- # - Maintains test dependencies
- # - Preserves decorators and docstrings
- test_class.move_to_file(new_file)
+# Grab a file
+file = codebase.get_file(filename)
+base_name = filename.replace('.py', '')
+
+# Group tests by subpath
+test_groups = {}
+for test_function in file.functions:
+ if test_function.name.startswith('test_'):
+ test_subpath = '_'.join(test_function.name.split('_')[:3])
+ if test_subpath not in test_groups:
+ test_groups[test_subpath] = []
+ test_groups[test_subpath].append(test_function)
+
+# Print and process each group
+for subpath, tests in test_groups.items():
+ print(f"\\n{subpath}/")
+ new_filename = f"{base_name}/{subpath}.py"
+
+ # Create file if it doesn't exist
+ if not codebase.has_file(new_filename):
+ new_file = codebase.create_file(new_filename)
+ file = codebase.get_file(new_filename)
+
+ # Move each test in the group
+ for test_function in tests:
+ print(f" - {test_function.name}")
+ test_function.move_to_file(new_file, strategy="add_back_edge")
# Commit changes to disk
codebase.commit()
-
```
@@ -299,14 +319,15 @@ if base_class:
Understand key concepts like working with files, functions, imports, and the
call graph to effectively manipulate code.
+
+ Iterate locally with your favorite IDE, work with a debugger and build sophisticated codemods
+
Learn how to use Codegen with Cursor, Devin, Windsurf, and more.
-
- Explore the complete API documentation for all Codegen classes and methods.
-
+
diff --git a/src/codegen/cli/commands/notebook/main.py b/src/codegen/cli/commands/notebook/main.py
index bff4a9d01..d00447288 100644
--- a/src/codegen/cli/commands/notebook/main.py
+++ b/src/codegen/cli/commands/notebook/main.py
@@ -20,9 +20,11 @@ def create_jupyter_dir() -> Path:
@click.command(name="notebook")
+@click.option("--background", is_flag=True, help="Run Jupyter Lab in the background")
+@click.option("--demo", is_flag=True, help="Create a demo notebook with FastAPI example code")
@requires_init
-def notebook_command(session: CodegenSession):
- """Open a Jupyter notebook with the current codebase loaded."""
+def notebook_command(session: CodegenSession, background: bool, demo: bool):
+ """Launch Jupyter Lab with a pre-configured notebook for exploring your codebase."""
with create_spinner("Setting up Jupyter environment...") as status:
venv = VenvManager()
@@ -30,7 +32,7 @@ def notebook_command(session: CodegenSession):
venv.ensure_jupyter()
jupyter_dir = create_jupyter_dir()
- notebook_path = create_notebook(jupyter_dir)
+ notebook_path = create_notebook(jupyter_dir, demo=demo)
status.update("Running Jupyter Lab...")
diff --git a/src/codegen/cli/utils/notebooks.py b/src/codegen/cli/utils/notebooks.py
index c4b047ebf..2414eeff7 100644
--- a/src/codegen/cli/utils/notebooks.py
+++ b/src/codegen/cli/utils/notebooks.py
@@ -1,7 +1,11 @@
import json
from pathlib import Path
+from typing import Any
-DEFAULT_NOTEBOOK_CONTENT = """from codegen import Codebase
+DEFAULT_CELLS = [
+ {
+ "cell_type": "code",
+ "source": """from codegen import Codebase
# Initialize codebase
codebase = Codebase('../../')
@@ -11,31 +15,199 @@
print("=" * 50)
print(f"๐ Total Files: {len(codebase.files)}")
print(f"โก Total Functions: {len(codebase.functions)}")
+print(f"๐ Total Imports: {len(codebase.imports)}")""".strip(),
+ }
+]
+
+DEMO_CELLS = [
+ ##### [ CODGEN DEMO ] #####
+ {
+ "cell_type": "markdown",
+ "source": """# Codegen Demo: FastAPI
+
+Welcome to [Codegen](https://docs.codegen.com)!
+
+This demo notebook will walk you through some features of Codegen applied to [FastAPI](https://github.com/fastapi/fastapi).
+
+See the [getting started](https://docs.codegen.com/introduction/getting-started) guide to learn more.""".strip(),
+ },
+ {
+ "cell_type": "code",
+ "source": """from codegen import Codebase
+
+# Initialize FastAPI codebase
+print('Cloning and parsing FastAPI to /tmp/codegen/fastapi...')
+codebase = Codebase.from_repo('fastapi/fastapi', commit="eab0653a346196bff6928710410890a300aee4ae")
+
+# To initialize a local codebase, use this constructor
+# codebase = Codebase("path/to/git/repo")""".strip(),
+ },
+ ##### [ CODEBASE ANALYSIS ] #####
+ {
+ "cell_type": "markdown",
+ "source": """# Codebase Analysis
+
+Let's do a quick codebase analysis!
+
+- Grab codebase content with [codebase.functions](https://docs.codegen.com/building-with-codegen/symbol-api) et al.
+- View inheritance hierarchies with [inhertance APIs](https://docs.codegen.com/building-with-codegen/class-api#working-with-inheritance)
+- Identify recursive functions by looking at [FunctionCalls](https://docs.codegen.com/building-with-codegen/function-calls-and-callsites)""".strip(),
+ },
+ {
+ "cell_type": "code",
+ "source": """# Print overall stats
+print("๐ FastAPI Analysis")
+print("=" * 50)
+print(f"๐ Total Classes: {len(codebase.classes)}")
+print(f"โก Total Functions: {len(codebase.functions)}")
print(f"๐ Total Imports: {len(codebase.imports)}")
-""".strip()
+
+# Find class with most inheritance
+if codebase.classes:
+ deepest_class = max(codebase.classes, key=lambda x: len(x.superclasses))
+ print(f"\\n๐ณ Class with most inheritance: {deepest_class.name}")
+ print(f" ๐ Chain Depth: {len(deepest_class.superclasses)}")
+ print(f" โ๏ธ Chain: {' -> '.join(s.name for s in deepest_class.superclasses)}")
+
+# Find first 5 recursive functions
+recursive = [f for f in codebase.functions
+ if any(call.name == f.name for call in f.function_calls)][:5]
+if recursive:
+ print(f"\\n๐ Recursive functions:")
+ for func in recursive:
+ print(f" - {func.name} ({func.file.filepath})")""".strip(),
+ },
+ ##### [ TEST DRILL DOWN ] #####
+ {
+ "cell_type": "markdown",
+ "source": """# Drilling Down on Tests
+
+Let's specifically drill into large test files, which can be cumbersome to manage:""".strip(),
+ },
+ {
+ "cell_type": "code",
+ "source": """from collections import Counter
+
+# Filter to all test functions and classes
+test_functions = [x for x in codebase.functions if x.name.startswith('test_')]
+
+print("๐งช Test Analysis")
+print("=" * 50)
+print(f"๐ Total Test Functions: {len(test_functions)}")
+print(f"๐ Tests per File: {len(test_functions) / len(codebase.files):.1f}")
+
+# Find files with the most tests
+print("\\n๐ Top Test Files by Count")
+print("-" * 50)
+file_test_counts = Counter([x.file for x in test_functions])
+for file, num_tests in file_test_counts.most_common()[:5]:
+ print(f"๐ {num_tests} test functions: {file.filepath}")
+ print(f" ๐ File Length: {len(file.source.split('\\n'))} lines")
+ print(f" ๐ก Functions: {len(file.functions)}")""".strip(),
+ },
+ ##### [ TEST SPLITTING ] #####
+ {
+ "cell_type": "markdown",
+ "source": """# Splitting Up Large Test Files
+
+Lets split up the largest test files into separate modules for better organization.
+
+This uses Codegen's [codebase.move_to_file(...)](https://docs.codegen.com/building-with-codegen/moving-symbols), which will:
+- update all imports
+- (optionally) move depenencies
+- do so very fast โก๏ธ
+
+While maintaining correctness.""",
+ },
+ ##### [ TEST SPLITTING ] #####
+ {
+ "cell_type": "code",
+ "source": """filename = 'tests/test_path.py'
+print(f"๐ฆ Splitting Test File: {filename}")
+print("=" * 50)
+
+# Grab a file
+file = codebase.get_file(filename)
+base_name = filename.replace('.py', '')
+
+# Group tests by subpath
+test_groups = {}
+for test_function in file.functions:
+ if test_function.name.startswith('test_'):
+ test_subpath = '_'.join(test_function.name.split('_')[:3])
+ if test_subpath not in test_groups:
+ test_groups[test_subpath] = []
+ test_groups[test_subpath].append(test_function)
+
+# Print and process each group
+for subpath, tests in test_groups.items():
+ print(f"\\n{subpath}/")
+ new_filename = f"{base_name}/{subpath}.py"
+
+ # Create file if it doesn't exist
+ if not codebase.has_file(new_filename):
+ new_file = codebase.create_file(new_filename)
+ file = codebase.get_file(new_filename)
+
+ # Move each test in the group
+ for test_function in tests:
+ print(f" - {test_function.name}")
+ test_function.move_to_file(new_file, strategy="add_back_edge")
+
+# Commit changes to disk
+codebase.commit()""".strip(),
+ },
+ ##### [ RESET ] #####
+ {
+ "cell_type": "markdown",
+ "source": """## View Changes
+
+You can now view changes by `cd /tmp/codegen/fastapi && git diff`
+
+Enjoy!
+
+# Reset
+
+Reset your codebase to it's initial state, discarding all changes
+
+Learn more in [commit and reset](https://docs.codegen.com/building-with-codegen/commit-and-reset).""".strip(),
+ },
+ {
+ "cell_type": "code",
+ "source": """codebase.reset()""".strip(),
+ },
+]
+
+
+def create_cells(cells_data: list[dict[str, str]]) -> list[dict[str, Any]]:
+ """Convert cell data into Jupyter notebook cell format."""
+ return [
+ {
+ "cell_type": cell["cell_type"],
+ "source": cell["source"],
+ "metadata": {},
+ "execution_count": None,
+ "outputs": [] if cell["cell_type"] == "code" else None,
+ }
+ for cell in cells_data
+ ]
-def create_notebook(jupyter_dir: Path) -> Path:
+def create_notebook(jupyter_dir: Path, demo: bool = False) -> Path:
"""Create a new Jupyter notebook if it doesn't exist.
Args:
jupyter_dir: Directory where the notebook should be created
+ demo: Whether to create a demo notebook with FastAPI example code
Returns:
Path to the created or existing notebook
"""
- notebook_path = jupyter_dir / "tmp.ipynb"
+ notebook_path = jupyter_dir / ("demo.ipynb" if demo else "tmp.ipynb")
if not notebook_path.exists():
+ cells = create_cells(DEMO_CELLS if demo else DEFAULT_CELLS)
notebook_content = {
- "cells": [
- {
- "cell_type": "code",
- "execution_count": None,
- "metadata": {},
- "outputs": [],
- "source": DEFAULT_NOTEBOOK_CONTENT,
- }
- ],
+ "cells": cells,
"metadata": {"kernelspec": {"display_name": "Python 3", "language": "python", "name": "python3"}},
"nbformat": 4,
"nbformat_minor": 4,