Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Release/0.1.7 #7

Closed
wants to merge 8 commits into from
Closed
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
3 changes: 2 additions & 1 deletion .flake8
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,5 @@ ignore = E501, W605

# E305: To add
per-file-ignores =
generate_toml.py: E305, E302
generate_toml.py: E305, E302
__init__.py: F401
2 changes: 1 addition & 1 deletion auto-pydantic-dev
Submodule auto-pydantic-dev updated from 34dedc to adc5e3
14 changes: 11 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,29 @@ build-backend = "setuptools.build_meta"

[project]
name = "crimson-auto-pydantic"
version = "0.1.5"
description = "Template Tools"
version = "0.1.7"
description = "Automatic Pydantic model generation and validation for function parameters and return types."
readme = "README.md"
authors = [
{ name="Sisung Kim", email="[email protected]" },
]

classifiers = [
"Development Status :: 4 - Beta",

"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",

"Intended Audience :: Developers",
"Topic :: Software Development :: Build Tools",

"Topic :: Software Development :: Libraries",

"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",

"Typing :: Typed",
]
dependencies = [
"pydantic",
Expand Down
3 changes: 2 additions & 1 deletion scripts/setup_env.sh
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ conda create --name auto-pydantic python=$PYTHON_VERSION -y
conda activate auto-pydantic

pip install -r requirements.txt

pip install -r requirements_test.txt
pip install -r requirements_dev.txt
73 changes: 36 additions & 37 deletions src/crimson/auto_pydantic/generator.py
Original file line number Diff line number Diff line change
@@ -1,61 +1,60 @@
from inflection import camelize
from typing import List, Generic, TypeVar, Union, Callable, Tuple
from typing import List, Generic, TypeVar, Union, Callable
from crimson.code_extractor import extract
from crimson.ast_dev_tool import safe_unparse, collect_nodes
from crimson.intelli_type import IntelliType
from crimson.intelli_type._replace_union import as_union
from inspect import getsource
import ast

T = TypeVar("T")


class Function_(IntelliType, Tuple[as_union, Callable, str, ast.FunctionDef], Generic[T]):
class Function_(IntelliType[Union[Callable, str, ast.FunctionDef]], Generic[T]):
"""
A versatile representation of a function that can handle various input types.

Example:
def func(arg1: int):
return arg1
def func(arg1: int):
return arg1

This class can handle any of the following representations:
- func (the function object itself)
- inspect.getsource(func) (the function's source code)
- ast.parse(inspect.getsource(func)).body[0] (the AST node of the function)
This class can handle any of the followings:
- func
- inspect.getsource(func)
- ast.parse(inspect.getsource(func)).body[0]

Key features:
- Robust handling of functions, even when they're not the first item in a module
- Capable of processing modules with multiple function definitions
- All auto-pydantic functions are designed to work flexibly with this class
- When multiple functions are present, the first function is used by default
- Robust handling of functions, even when they're not the first item in a module
- Capable of processing modules with multiple function definitions
- All auto-pydantic functions are designed to work flexibly with this class
- When multiple functions are present, the first function is used by default

Note: This flexibility allows for easier integration and usage across different
scenarios in the auto-pydantic module.
"""


class Constructor_(IntelliType, str, Generic[T]):
class Constructor_(IntelliType[str], Generic[T]):
"""
Represents the constructor code for a Pydantic model.

This class encapsulates the string representation of a Pydantic model's constructor.
It allows the model to mirror the structure of the original function it's based on.

Example:
Original function:
def func2(arg1: int, *args: Tuple[int, int], arg2: str = "hi", arg3: int = 1, **kwargs) -> str:
return "bye"
Original function:
def func2(arg1: int, *args: Tuple[int, int], arg2: str = "hi", arg3: int = 1, **kwargs) -> str:
return "bye"

Corresponding constructor:
def __init__(self, arg1: int, *args: Tuple[int, int], arg2: str='hi', arg3: int=1, **kwargs):
super().__init__(arg1=arg1, args=args, arg2=arg2, arg3=arg3, kwargs=kwargs)
Corresponding constructor:
def __init__(self, arg1: int, *args: Tuple[int, int], arg2: str='hi', arg3: int=1, **kwargs):
super().__init__(arg1=arg1, args=args, arg2=arg2, arg3=arg3, kwargs=kwargs)

This constructor ensures that the Pydantic model can be instantiated with the same
signature as the original function, maintaining consistency in parameter handling.
"""


class InputProps_(IntelliType, str, Generic[T]):
class InputProps_(IntelliType[str], Generic[T]):
"""
Represents the input properties of a Pydantic model based on a function's parameters.

Expand All @@ -64,47 +63,47 @@ class InputProps_(IntelliType, str, Generic[T]):

Example:
For the function:
def func2(arg1: int, *args: Tuple[int, int], arg2: str = "hi", arg3: int = 1, **kwargs) -> str:
return "bye"
def func2(arg1: int, *args: Tuple[int, int], arg2: str = "hi", arg3: int = 1, **kwargs) -> str:
return "bye"

The corresponding InputProps model would be:
class Func2InputProps(BaseModel):
arg1: int = Field(...)
args: Tuple[int, int] = Field(default=())
arg2: str = Field(default="'hi'")
arg3: int = Field(default='1')
kwargs: Any = Field(default={})
class Func2InputProps(BaseModel):
arg1: int = Field(...)
args: Tuple[int, int] = Field(default=())
arg2: str = Field(default="'hi'")
arg3: int = Field(default='1')
kwargs: Any = Field(default={})

{optional_constructor}
{optional_constructor}

Note: The {optional_constructor} placeholder can be replaced with an actual
constructor if needed, allowing for flexible model creation.
"""


class OutputProps_(IntelliType, str, Generic[T]):
class OutputProps_(IntelliType[str], Generic[T]):
"""
Represents the output properties of a Pydantic model based on a function's return type.

This class contains the string representation of a Pydantic model class that
encapsulates the return type of a target function.

Example:
For the function:
def func2(arg1: int, *args: Tuple[int, int], arg2: str = "hi", arg3: int = 1, **kwargs) -> str:
return "bye"
For the function:
def func2(arg1: int, *args: Tuple[int, int], arg2: str = "hi", arg3: int = 1, **kwargs) -> str:
return "bye"

The corresponding OutputProps model would be:
class Func2OutputProps(BaseModel):
return: str
class Func2OutputProps(BaseModel):
return: str

This model standardizes the function's output, making it easier to validate
and work with the return value in a type-safe manner.
"""


def generate_constructor(
function: Function_[Union[Callable, str, ast.FunctionDef]], indent: int = 4
function: Union[Callable, str, ast.FunctionDef], indent: int = 4
) -> Constructor_[str]:
"""
Generate a constructor string for a Pydantic model based on the given function.
Expand Down
22 changes: 9 additions & 13 deletions src/crimson/auto_pydantic/generator_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
T = TypeVar("T")


class Func_(IntelliType, Callable, Generic[T]):
class Func_(IntelliType[Callable], Generic[T]):
"""
Any function you defined.

Expand All @@ -22,34 +22,28 @@ def my_func(arg1: int, arg2: str) -> str:
"""


class FrameType_:
"""Dummy of FrameType"""


class CurrentFrame_(IntelliType, FrameType_, Generic[T]):
class CurrentFrame_(IntelliType[FrameType], Generic[T]):
"""
from inspect import currentframe()

currentframe() : CurrentFrame_[FrameType]

"""

_annotation = FrameType


class InputPropsModel_(IntelliType, BaseModel, Generic[T]):
class InputPropsModel_(IntelliType[BaseModel], Generic[T]):
"""
The generated model using the inputprops string from 'generator'.
"""


class _NameSpace_(IntelliType, Dict[str, Any], Generic[T]):
class _NameSpace_(IntelliType[Dict[str, Any]], Generic[T]):
"""
It contains variables to be used to generate inputprops, returnprops, and constructor
"""


class _FuncName_(IntelliType, str, Generic[T]):
class _FuncName_(IntelliType[str], Generic[T]):
"""
It might be the literal name of a function.
However, it must be Camel case in the real usage.
Expand All @@ -61,13 +55,15 @@ def snake_case():
"""


class _FunctionNode_(IntelliType, ast.FunctionDef, Generic[T]):
class _FunctionNode_(IntelliType[ast.FunctionDef], Generic[T]):
"""
The ast module uses it to extract the props of Func_[str].
"""


def generate_inputprops_model(func: Func_[Callable], currentframe: CurrentFrame_[FrameType], *args: Any, **kwargs: Any) -> InputPropsModel_[BaseModel]:
def generate_inputprops_model(
func: Func_[Callable], currentframe: CurrentFrame_[FrameType], *args: Any, **kwargs: Any
) -> InputPropsModel_[BaseModel]:
namespace = _prepare_namespace(currentframe, args, kwargs)
function_node = _get_function_node(func)
func_name = _generate_input_props_name(func.__name__)
Expand Down
2 changes: 1 addition & 1 deletion src/crimson/auto_pydantic/validator.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ class Bool_:
"""


class Validated_(IntelliType, Bool_, Generic[T]):
class Validated_(IntelliType[bool], Generic[T]):
"""
It doesn't mean whether the validation was successful or not.
It rather shows the validate function was conducted or just passed.
Expand Down
3 changes: 2 additions & 1 deletion test/test_validator.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import pytest
from crimson.auto_pydantic.validator import validate, config
from inspect import currentframe
from inspect import currentframe, getsource


@pytest.fixture
Expand Down Expand Up @@ -34,6 +34,7 @@ def init_validate_function(arg1: int, arg2: str = "default") -> str:

def test_validate_simple_valid():
# This should not raise any exception
source = getsource(simple_function)
validate(simple_function, currentframe(), arg1=1, arg2="test")


Expand Down
Loading