Skip to content

Commit

Permalink
Merge branch 'release/0.1.4'
Browse files Browse the repository at this point in the history
  • Loading branch information
crimson206 committed Jul 4, 2024
2 parents 85210d1 + 8429508 commit 150c453
Show file tree
Hide file tree
Showing 13 changed files with 402 additions and 108 deletions.
1 change: 1 addition & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
include src/crimson/auto_pydantic/test.zip
17 changes: 2 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ auto-pydantic is a Python module that provides automatic Pydantic model generati

## Installation

To install auto-pydantic, you can use pip:
To install crimson-auto-pydantic, you can use pip:

```
pip install auto-pydantic
pip install acrimson-auto-pydantic
```

## Usage
Expand Down Expand Up @@ -62,16 +62,3 @@ my_function(1, "test")
# This will raise a validation error
my_function("not an int", "test")
```

## API Reference

### generator.py

- `generate_input_props(function)`: Generate a Pydantic model for function input parameters
- `generate_output_props(function)`: Generate a Pydantic model for function return type
- `generate_constructor(function)`: Generate a constructor for the Pydantic model

### validator.py

- `validate(func, currentframe, *args, **kwargs)`: Validate function inputs against the generated Pydantic model

5 changes: 4 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"

[project]
name = "crimson-auto-pydantic"
version = "0.1.3"
version = "0.1.4"
description = "Template Tools"
readme = "README.md"
authors = [
Expand Down Expand Up @@ -35,3 +35,6 @@ requires-python = ">=3.9"

[tool.black]
line-length = 120

[tool.setuptools]
include-package-data = true
21 changes: 8 additions & 13 deletions src/crimson/auto_pydantic/__init__.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,14 @@
from .generator import generate_constructor, generate_output_props, generate_input_props
from .generator_model import generate_inputprops_model
from .validator import validate
from .beta.test_loader import _extract_zip_to_dir, _create_zip_from_dir
from pathlib import Path

import zipfile
import os

def _load_test_zip():
test_dir = "../../test"
_create_zip_from_dir(test_dir, Path(__file__).parent, 'test.zip', includes=['.py'], excludes=['__pycache__', '.pytest_cache'])

def extract_tests(destination):
"""
ZIP 파일에서 test 폴더를 추출합니다.

:param destination: test 폴더를 추출할 목적지 경로
"""
package_dir = os.path.dirname(__file__)
zip_path = os.path.join(package_dir, "test.zip")

with zipfile.ZipFile(zip_path, "r") as zip_ref:
zip_ref.extractall(destination)
print(f"Tests have been extracted to {destination}")
def extract_tests(out_dir):
_extract_zip_to_dir(Path(__file__).parent / 'test.zip', out_dir)
Empty file.
49 changes: 49 additions & 0 deletions src/crimson/auto_pydantic/beta/test_loader/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
from crimson.file_loader.utils import filter_paths
from pathlib import Path
import zipfile


def _create_zip_from_dir(input_dir: str, output_dir: str, zip_name: str = "output.zip", includes=[], excludes=[]) -> str:
"""
주어진 디렉토리의 내용을 ZIP 파일로 만들어 지정된 출력 디렉토리에 저장합니다.
:param input_dir: ZIP으로 만들 디렉토리의 경로
:param output_dir: ZIP 파일을 저장할 디렉토리의 경로
:param zip_name: 생성할 ZIP 파일의 이름 (기본값: "output.zip")
:return: 생성된 ZIP 파일의 전체 경로
"""
paths = filter_paths(input_dir, includes, excludes)
output_path = Path(output_dir).resolve()

output_path.mkdir(parents=True, exist_ok=True)

zip_path = output_path / zip_name

# ZIP 파일 생성
with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zipf:
for path in paths:
zipf.write(path, arcname=Path(path).name)

return str(zip_path)


def _extract_zip_to_dir(zip_path: str, output_dir: str) -> str:
"""
주어진 ZIP 파일을 지정된 출력 디렉토리에 풉니다.
:param zip_path: 압축 해제할 ZIP 파일의 경로
:param output_dir: ZIP 파일의 내용을 풀어낼 디렉토리의 경로
:return: ZIP 파일이 압축 해제된 디렉토리의 전체 경로
"""
# ZIP 파일 경로와 출력 디렉토리 경로를 Path 객체로 변환
zip_file = Path(zip_path).resolve()
output_path = Path(output_dir).resolve()

# 출력 디렉토리가 존재하지 않으면 생성
output_path.mkdir(parents=True, exist_ok=True)

# ZIP 파일 압축 해제
with zipfile.ZipFile(zip_file, 'r') as zip_ref:
zip_ref.extractall(output_path)

return str(output_path)
116 changes: 77 additions & 39 deletions src/crimson/auto_pydantic/generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,80 +12,106 @@

class Function_(IntelliType, Tuple[as_union, Callable, str, ast.FunctionDef], Generic[T]):
"""
def func(arg1:int):
return arg1
A versatile representation of a function that can handle various input types.
It can be any of them among,
Example:
def func(arg1: int):
return arg1
func
function_code = inspect.getsource(func)
function_node = ast.parse(function_code)
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)
It is safe even if,
- The function is not places at the first
- There are many functions in it
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
All the functions from auto-pydantic will deal with them flexibly.
If there are many functions in it, the first function is the object this module uses.
Note: This flexibility allows for easier integration and usage across different
scenarios in the auto-pydantic module.
"""


class Constructor_(IntelliType, str, Generic[T]):
"""
It is the code lines of the constructor of the pydantic model.
Represents the constructor code for a Pydantic model.
It allows you to use the model in the same structure where you use the function.
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.
function:
def func2(arg1: int, *args: Tuple[int, int], arg2: str = "hi", arg3: int = 1, **kwargs, ) -> str:
return "bye"
Example:
Original function:
def func2(arg1: int, *args: Tuple[int, int], arg2: str = "hi", arg3: int = 1, **kwargs) -> str:
return "bye"
constructor: str
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]):
"""
It is the code lines of the InputProps model for the target function's input parameters.
Represents the input properties of a Pydantic model based on a function's parameters.
This class contains the string representation of a Pydantic model class that
encapsulates the input parameters of a target function.
function:
def func2(arg1: int, *args: Tuple[int, int], arg2: str = "hi", arg3: int = 1, **kwargs, ) -> str:
return "bye"
Example:
For the function:
def func2(arg1: int, *args: Tuple[int, int], arg2: str = "hi", arg3: int = 1, **kwargs) -> str:
return "bye"
input_props: str
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={})
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={})
\{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]):
"""
It is the code lines of the OutputProps model for the target function's return annotation.
Represents the output properties of a Pydantic model based on a function's return type.
function:
def func2(arg1: int, *args: Tuple[int, int], arg2: str = "hi", arg3: int = 1, **kwargs, ) -> str:
return "bye"
This class contains the string representation of a Pydantic model class that
encapsulates the return type of a target function.
output_props: str
class Func2OutputProps(BaseModel):
return: str
Example:
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
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
) -> Constructor_[str]:
"""
Generate a constructor string for a Pydantic model based on the given function.
This function creates a constructor that mirrors the input function's signature,
allowing for seamless integration with Pydantic models.
"""
function_node = _convert_to_FunctionDef(function)
func_spec = extract.extract_func_spec(function_node)
indent = " " * indent
Expand All @@ -110,6 +136,12 @@ def generate_constructor(
def generate_input_props(
function: Function_[Union[Callable, str, ast.FunctionDef]], add_constructor: bool = True
) -> InputProps_[str]:
"""
Generate input properties string for a Pydantic model based on the given function.
This function creates a Pydantic model class that represents the input parameters
of the given function, including type annotations and default values.
"""
function_node: ast.FunctionDef = _convert_to_FunctionDef(function)
func_spec = extract.extract_func_spec(function_node)

Expand Down Expand Up @@ -145,6 +177,12 @@ def generate_input_props(


def generate_output_props(function: Function_[Union[Callable, str, ast.FunctionDef]]) -> OutputProps_[str]:
"""
Generate output properties string for a Pydantic model based on the given function.
This function creates a Pydantic model class that represents the return type
of the given function, encapsulating the output in a standardized format.
"""
function_node = _convert_to_FunctionDef(function)

func_spec = extract.extract_func_spec(function_node)
Expand Down
Loading

0 comments on commit 150c453

Please sign in to comment.