Skip to content

Commit 9db2c43

Browse files
fix: update dict_to_schema example with proper implementation
Co-Authored-By: [email protected] <[email protected]>
1 parent 04ce83b commit 9db2c43

File tree

2 files changed

+61
-74
lines changed

2 files changed

+61
-74
lines changed

examples/dict_to_schema/README.md

Lines changed: 11 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -2,63 +2,38 @@
22

33
This example demonstrates how to automatically convert Python dictionary literals into Pydantic models. The codemod makes this process simple by handling all the tedious manual updates automatically.
44

5-
> [!NOTE]
6-
> View example transformations created by this codemod on the `modal-labs/modal-client` repository [here](https://www.codegen.sh/codemod/6b5f2dfa-948a-4953-b283-9bd4b8545632/public/diff).
7-
85
## How the Conversion Script Works
96

107
The script (`run.py`) automates the entire conversion process in a few key steps:
118

12-
1. **Codebase Loading**
13-
```python
14-
codebase = Codebase.from_repo("modal-labs/modal-client")
15-
```
16-
- Loads your codebase into Codegen's intelligent code analysis engine
17-
- Provides a simple SDK for making codebase-wide changes
18-
- Supports any Git repository as input
19-
20-
2. **Dictionary Detection**
21-
```python
22-
if "{" in global_var.source and "}" in global_var.source:
23-
dict_content = global_var.value.source.strip("{}")
24-
```
9+
1. **Dictionary Detection**
2510
- Automatically identifies dictionary literals in your code
2611
- Processes both global variables and class attributes
2712
- Skips empty dictionaries to avoid unnecessary conversions
2813

29-
3. **Schema Creation**
30-
```python
31-
class_name = global_var.name.title() + "Schema"
32-
model_def = f"""class {class_name}(BaseModel):
33-
{dict_content.replace(",", "\n ")}"""
34-
```
14+
2. **Schema Creation**
3515
- Generates meaningful model names based on variable names
3616
- Converts dictionary key-value pairs to class attributes
3717
- Maintains proper Python indentation
3818

39-
4. **Code Updates**
40-
```python
41-
global_var.insert_before(model_def + "\n\n")
42-
global_var.set_value(f"{class_name}(**{global_var.value.source})")
43-
```
19+
3. **Code Updates**
4420
- Inserts new Pydantic models in appropriate locations
4521
- Updates dictionary assignments to use the new models
4622
- Automatically adds required Pydantic imports
4723

48-
49-
## Common Conversion Patterns
24+
## Example Transformations
5025

5126
### Global Variables
5227
```python
5328
# Before
54-
config = {"host": "localhost", "port": 8080}
29+
app_config = {"host": "localhost", "port": 8080}
5530

5631
# After
57-
class ConfigSchema(BaseModel):
32+
class AppConfigSchema(BaseModel):
5833
host: str = "localhost"
5934
port: int = 8080
6035

61-
config = ConfigSchema(**{"host": "localhost", "port": 8080})
36+
app_config = AppConfigSchema(**{"host": "localhost", "port": 8080})
6237
```
6338

6439
### Class Attributes
@@ -79,18 +54,14 @@ class Service:
7954
## Running the Conversion
8055

8156
```bash
82-
# Install Codegen
83-
pip install codegen
57+
# Initialize Codegen in your project
58+
codegen init
8459

85-
# Run the conversion
86-
python run.py
60+
# Run the codemod
61+
codegen run dict_to_schema
8762
```
8863

8964
## Learn More
9065

9166
- [Pydantic Documentation](https://docs.pydantic.dev/)
9267
- [Codegen Documentation](https://docs.codegen.com)
93-
94-
## Contributing
95-
96-
Feel free to submit issues and enhancement requests!

examples/dict_to_schema/run.py

Lines changed: 50 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -13,46 +13,50 @@ def run(codebase: Codebase):
1313
3. Updates the assignments to use the new models
1414
4. Adds necessary Pydantic imports
1515
"""
16-
# Track statistics
1716
files_modified = 0
1817
models_created = 0
1918

20-
# Iterate through all files in the codebase
21-
for file in codebase.files:
19+
total_files = len(codebase.files)
20+
print(f"\n📁 Scanning {total_files} files for dictionary literals...")
21+
22+
for i, file in enumerate(codebase.files, 1):
2223
needs_imports = False
2324
file_modified = False
2425

25-
# Look for dictionary assignments in global variables
26+
print(f"\n🔍 Checking file {i}/{total_files}: {file.path}")
27+
2628
for global_var in file.global_vars:
2729
try:
2830
if "{" in global_var.source and "}" in global_var.source:
2931
dict_content = global_var.value.source.strip("{}")
3032
if not dict_content.strip():
3133
continue
3234

33-
# Convert dict to Pydantic model
3435
class_name = global_var.name.title() + "Schema"
3536
model_def = f"""class {class_name}(BaseModel):
3637
{dict_content.replace(",", "\n ")}"""
3738

38-
print(f"\nConverting '{global_var.name}' to schema")
39-
print("\nOriginal code:")
40-
print(global_var.source)
41-
print("\nNew code:")
42-
print(model_def)
43-
print(f"{class_name}(**{global_var.value.source})")
44-
print("-" * 50)
39+
print("\n" + "=" * 60)
40+
print(f"🔄 Converting global variable '{global_var.name}' to schema")
41+
print("=" * 60)
42+
print("📝 Original code:")
43+
print(f" {global_var.name} = {global_var.value.source}")
44+
print("\n✨ Generated schema:")
45+
print(" " + model_def.replace("\n", "\n "))
46+
print("\n✅ Updated code:")
47+
print(f" {global_var.name} = {class_name}(**{global_var.value.source})")
48+
print("=" * 60)
4549

46-
# Insert model and update assignment
4750
global_var.insert_before(model_def + "\n\n")
4851
global_var.set_value(f"{class_name}(**{global_var.value.source})")
4952
needs_imports = True
5053
models_created += 1
5154
file_modified = True
5255
except Exception as e:
53-
print(f"Error processing global variable {global_var.name}: {str(e)}")
56+
print(f"\n❌ Error processing global variable '{global_var.name}':")
57+
print(f" {str(e)}")
58+
print(" Skipping this variable and continuing...\n")
5459

55-
# Look for dictionary assignments in class attributes
5660
for cls in file.classes:
5761
for attr in cls.attributes:
5862
try:
@@ -61,43 +65,55 @@ def run(codebase: Codebase):
6165
if not dict_content.strip():
6266
continue
6367

64-
# Convert dict to Pydantic model
6568
class_name = attr.name.title() + "Schema"
6669
model_def = f"""class {class_name}(BaseModel):
6770
{dict_content.replace(",", "\n ")}"""
6871

69-
print(f"\nConverting'{attr.name}' to schema")
70-
print("\nOriginal code:")
71-
print(attr.source)
72-
print("\nNew code:")
73-
print(model_def)
74-
print(f"{class_name}(**{attr.value.source})")
75-
print("-" * 50)
72+
print("\n" + "=" * 60)
73+
print(f"🔄 Converting class attribute '{cls.name}.{attr.name}' to schema")
74+
print("=" * 60)
75+
print("📝 Original code:")
76+
print(f" class {cls.name}:")
77+
print(f" {attr.name} = {attr.value.source}")
78+
print("\n✨ Generated schema:")
79+
print(" " + model_def.replace("\n", "\n "))
80+
print("\n✅ Updated code:")
81+
print(f" class {cls.name}:")
82+
print(f" {attr.name} = {class_name}(**{attr.value.source})")
83+
print("=" * 60)
7684

77-
# Insert model and update attribute
7885
cls.insert_before(model_def + "\n\n")
7986
attr.set_value(f"{class_name}(**{attr.value.source})")
8087
needs_imports = True
8188
models_created += 1
8289
file_modified = True
8390
except Exception as e:
84-
print(f"Error processing attribute {attr.name} in class {cls.name}: {str(e)}")
91+
print(f"\n❌ Error processing attribute '{attr.name}' in class '{cls.name}':")
92+
print(f" {str(e)}")
93+
print(" Skipping this attribute and continuing...\n")
8594

86-
# Add imports if needed
8795
if needs_imports:
96+
print(f" ➕ Adding Pydantic imports to {file.path}")
8897
file.add_import_from_import_string("from pydantic import BaseModel")
8998

9099
if file_modified:
100+
print(f" ✅ Successfully modified {file.path}")
91101
files_modified += 1
92102

93-
print("\nModification complete:")
94-
print(f"Files modified: {files_modified}")
95-
print(f"Schemas created: {models_created}")
96-
103+
print("\n" + "=" * 60)
104+
print("📊 Summary of Changes")
105+
print("=" * 60)
106+
print(f"✨ Files modified: {files_modified}")
107+
print(f"🔄 Schemas created: {models_created}")
108+
print("=" * 60)
97109

98110
if __name__ == "__main__":
99-
print("Initializing codebase...")
100-
codebase = Codebase.from_repo("modal-labs/modal-client", commit="81941c24897889a2ff2f627c693fa734967e693c", programming_language=ProgrammingLanguage.PYTHON)
101-
102-
print("Running codemod...")
111+
print("\n🔍 Initializing codebase...")
112+
codebase = Codebase.from_repo("fastapi/fastapi", programming_language=ProgrammingLanguage.PYTHON)
113+
print("\n🚀 Running dict-to-pydantic-schema codemod...")
114+
print("\nℹ️ This codemod will:")
115+
print(" 1. Find dictionary literals in your code")
116+
print(" 2. Convert them to Pydantic models")
117+
print(" 3. Update assignments to use the new models")
118+
print(" 4. Add required imports\n")
103119
run(codebase)

0 commit comments

Comments
 (0)