#4657 Skill Markdown Files with Pydantic Class(es) Included
#4657 Skill Markdown Files with Pydantic Class(es) Included
#4657 Can a class be created dynamically from a text string in Python?
Yes, absolutely! You can create a class dynamically from a text string in Python. Depending on your safety requirements and use case, there are two primary ways to do this:
1. Using type() (The Native & Clean Approach)
If you already know the attributes and methods you want to attach, Python’s built-in type() function lets you construct classes on the fly without evaluating raw strings.
When called with three arguments—type(name, bases, dict)—it creates a brand new class:
# Define a method to attach to the new class
def speak(self):
return f"Hello, I am {self.name}!"
# Dynamically create the class named 'Dog'
class_name = "Dog"
attributes = {
"species": "Canine",
"__init__": lambda self, name: setattr(self, "name", name),
"bark": speak
}
# Syntax: type(className, superclasses, attributeDict)
DynamicDog = type(class_name, (object,), attributes)
# Usage
my_dog = DynamicDog("Buddy")
print(my_dog.bark()) # Output: Hello, I am Buddy!
print(my_dog.species) # Output: Canine
2. Using exec() (Executing Code from a String)
If you have a complete class definition written out as a literal string of Python code, you can execute it using exec().
class_code = """
class DynamicCar:
def __init__(self, make, model):
self.make = make
self.model = model
def drive(self):
return f"Vroom! Driving a {self.make} {self.model}."
"""
# Create a dictionary to act as the local scope
namespace = {}
# Execute the code string within that namespace
exec(class_code, namespace)
# Retrieve the class object from the namespace
DynamicCar = namespace["DynamicCar"]
# Usage
my_car = DynamicCar("Tesla", "Model 3")
print(my_car.drive()) # Output: Vroom! Driving a Tesla Model 3.
Comparison & Safety
| Method | Best For | Security Risk |
| type() | Constructing classes programmatically with known structures | Low |
| exec() | Parsing raw Python source code strings | High (if handling untrusted input) |
Warning: Be extremely cautious when using
exec()with unvalidated input from users, API requests, or external files, as it allows arbitrary code execution.
Create a class for use by Pydantic
When working with Pydantic, the best way to construct a class dynamically depends on what you are starting with:
pydantic.create_model: Use this if you are building fields programmatically (using Python types, defaults, orField()).exec()withBaseModel: Use this if you have a literal Python code string containing a class definition.
Method 1: Using pydantic.create_model (Recommended)
Pydantic provides a built-in helper called create_model specifically designed for dynamic model creation.
Fields are passed as keyword arguments using tuples formatted as (type, default_value) or (type, Field(...)). Use ... (Ellipsis) or Field(..., ...) to denote a required field.
from pydantic import Field, create_model
# 1. Define fields programmatically
# Syntax: field_name=(type, default_or_Field)
fields = {
"username": (str, ...), # Required
"age": (int, Field(default=18, ge=0)), # Optional with default and validation
"is_active": (bool, True), # Optional with default
}
# 2. Dynamically create the Pydantic class
DynamicUser = create_model("DynamicUser", **fields)
# 3. Instantiate and validate
user = DynamicUser(username="alice", age=25)
print(user.model_dump())
# Output: {'username': 'alice', 'age': 25, 'is_active': True}
Method 2: From a String of Python Code (Using exec)
If your class definition is supplied as a raw code string, you can execute it within a local namespace where BaseModel is accessible.
from pydantic import BaseModel
class_code = """
class DynamicItem(BaseModel):
name: str
price: float
in_stock: bool = True
def summary(self) -> str:
return f"{self.name} costs ${self.price:.2f}"
"""
# Provide Pydantic's BaseModel in the execution context
namespace = {"BaseModel": BaseModel}
exec(class_code, namespace)
# Fetch the generated class
DynamicItem = namespace["DynamicItem"]
# Instantiate and validate
item = DynamicItem(name="Coffee Mug", price=12.99)
print(item.summary()) # Output: Coffee Mug costs $12.99
print(item.model_dump_json())
# Output: {"name":"Coffee Mug","price":12.99,"in_stock":true}
Summary Tip
Use
create_model()when building schemas dynamically from JSON input, databases, or runtime parameters.Use
exec()only if you are parsing literal Python code blocks from external configurations (and make sure to sanitize untrusted input!).
So if I create a skill markdown file, I could include a class definition which I could parse from Python and declare in-line
Yes, exactly! That pattern is super popular in agentic workflows, plugin systems, and LLM tool-calling engines (like Semantic Kernel or custom AI framework setups).
You can embed a block of Python code inside your Markdown "Skill" file, parse it out, and load it dynamically into your running Python session.
Here is a complete, practical pattern for doing this cleanly:
1. The Skill Markdown File (skills/calculator.md)
In your Markdown file, you can write the class definition inside a standard triple-backtick Python code block:
# Calculator Skill
This skill provides basic mathematical operations.
```python
from pydantic import BaseModel, Field
class CalculateSchema(BaseModel):
operation: str = Field(description="The math operation to perform (+, -, *, /)")
a: float = Field(description="First number")
b: float = Field(description="Second number")
def execute() -> float:
if self.operation == "+": return self.a + self.b
if self.operation == "-": return self.a - self.b
if self.operation == "*": return self.a * self.b
if self.operation == "/": return self.a / self.b
raise ValueError("Invalid operation")
```
2. Parsing and In-Line Execution in Python
You can use regular expressions to extract the Python code block, execute it in an isolated namespace, and retrieve your dynamic Pydantic class:
import re
from pydantic import BaseModel, Field
def load_pydantic_class_from_markdown(md_filepath: str, class_name: str):
with open(md_filepath, "r", encoding="utf-8") as f:
content = f.read()
# Regex to extract code from ```python ... ``` blocks
code_match = re.search(r"```python\n(.*?\n)```", content, re.DOTALL)
if not code_match:
raise ValueError("No python code block found in markdown file.")
code_string = code_match.group(1)
# Provide necessary imports/globals into the execution namespace
namespace = {
"BaseModel": BaseModel,
"Field": Field,
}
# Execute the code block in our controlled namespace
exec(code_string, namespace)
# Return the target class
return namespace[class_name]
# Usage:
CalculateSchema = load_pydantic_class_from_markdown("skills/calculator.md", "CalculateSchema")
# Validate data directly into the dynamically parsed model
calc_input = CalculateSchema(operation="*", a=7.0, b=6.0)
print(calc_input.execute()) # Output: 42.0
Best Practices for this Pattern
Explicit Imports: Include needed imports (like
from pydantic import BaseModel) inside the Markdown code block itself so the block is self-contained. Then passglobals()or standard modules as the base namespace inexec().Schema Inspection: Because it's a real Pydantic model once loaded, you can call
CalculateSchema.model_json_schema()to generate structured tool schemas for OpenAI, Anthropic, or local LLMs automatically.Security Note: Make sure these
.mdfiles are coming from a trusted source (e.g., your local codebase or a secure repo), sinceexec()will run whatever Python code is inside those backticks!
From <https://gemini.google.com/app/1281184851a72d89> Google Gemini (3.6 Flash)
Comments
Post a Comment