mirror of
https://github.com/PrefectHQ/fastmcp.git
synced 2026-08-20 04:24:17 +02:00
fix: unintended type convert
The function tries to convert `str`` into `dict` without validate if it is suitable for the type annotation. This will cause unintended type converting. Refs: #251
This commit is contained in:
parent
7df8db451d
commit
c8dface36a
2 changed files with 72 additions and 4 deletions
|
|
@ -7,7 +7,15 @@ from typing import (
|
|||
ForwardRef,
|
||||
)
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field, WithJsonSchema, create_model
|
||||
from pydantic import (
|
||||
BaseModel,
|
||||
ConfigDict,
|
||||
Field,
|
||||
TypeAdapter,
|
||||
ValidationError,
|
||||
WithJsonSchema,
|
||||
create_model,
|
||||
)
|
||||
from pydantic._internal._typing_extra import eval_type_backport
|
||||
from pydantic.fields import FieldInfo
|
||||
from pydantic_core import PydanticUndefined
|
||||
|
|
@ -80,14 +88,18 @@ class FuncMetadata(BaseModel):
|
|||
dicts (JSON objects) as JSON strings, which can be pre-parsed here.
|
||||
"""
|
||||
new_data = data.copy() # Shallow copy
|
||||
for field_name, _field_info in self.arg_model.model_fields.items():
|
||||
for field_name, field_info in self.arg_model.model_fields.items():
|
||||
if field_name not in data.keys():
|
||||
continue
|
||||
if isinstance(data[field_name], str):
|
||||
try:
|
||||
pre_parsed = json.loads(data[field_name])
|
||||
except json.JSONDecodeError:
|
||||
continue # Not JSON - skip
|
||||
|
||||
# Check if the pre_parsed value is valid for the field
|
||||
validator = TypeAdapter(field_info.annotation)
|
||||
validator.validate_python(pre_parsed)
|
||||
except (json.JSONDecodeError, ValidationError):
|
||||
continue # Not JSON or invalid for the field
|
||||
if isinstance(pre_parsed, str | int | float):
|
||||
# This is likely that the raw value is e.g. `"hello"` which we
|
||||
# Should really be parsed as '"hello"' in Python - but if we parse
|
||||
|
|
|
|||
|
|
@ -174,6 +174,62 @@ def test_str_vs_list_str():
|
|||
assert result["str_or_list"] == ["hello", "world"]
|
||||
|
||||
|
||||
def test_keep_str_as_str():
|
||||
"""Test that string arguments are kept as strings"""
|
||||
|
||||
def func_with_str_types(string: str):
|
||||
return string
|
||||
|
||||
meta = func_metadata(func_with_str_types)
|
||||
result = meta.pre_parse_json(
|
||||
{"string": "{'nice to meet you': 'hello', 'goodbye': 5}"}
|
||||
)
|
||||
assert result["string"] == "{'nice to meet you': 'hello', 'goodbye': 5}"
|
||||
|
||||
|
||||
def test_keep_str_union_as_str():
|
||||
"""Test that string arguments are kept as strings"""
|
||||
|
||||
def func_with_str_types(string: str | dict[int, str] | None):
|
||||
return string
|
||||
|
||||
meta = func_metadata(func_with_str_types)
|
||||
result = meta.pre_parse_json(
|
||||
{"string": "{'nice to meet you': 'hello', 'goodbye': 5}"}
|
||||
)
|
||||
assert result["string"] == "{'nice to meet you': 'hello', 'goodbye': 5}"
|
||||
|
||||
|
||||
def test_keep_str_complex_type_as_str():
|
||||
"""Test that string arguments are kept as strings because it's invalid for the field"""
|
||||
|
||||
class SomeModel(BaseModel):
|
||||
x: int
|
||||
y: dict[int, str]
|
||||
|
||||
def func_with_str_types(string: str | SomeModel | None):
|
||||
return string
|
||||
|
||||
meta = func_metadata(func_with_str_types)
|
||||
result = meta.pre_parse_json({"string": '{"x": 1, "y": {"invalid": "hello"}}'})
|
||||
assert result["string"] == '{"x": 1, "y": {"invalid": "hello"}}'
|
||||
|
||||
|
||||
def test_convert_str_to_complex_type():
|
||||
"""Test that string arguments are converted to the complex type because it's valid for the field"""
|
||||
|
||||
class SomeModel(BaseModel):
|
||||
x: int
|
||||
y: dict[int, str]
|
||||
|
||||
def func_with_str_types(string: str | SomeModel | None):
|
||||
return string
|
||||
|
||||
meta = func_metadata(func_with_str_types)
|
||||
result = meta.pre_parse_json({"string": '{"x": 1, "y": {"1": "hello"}}'})
|
||||
assert result["string"] == {"x": 1, "y": {"1": "hello"}}
|
||||
|
||||
|
||||
def test_skip_names():
|
||||
"""Test that skipped parameters are not included in the model"""
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue