7 Commits

Author SHA1 Message Date
a75ad61a21 Merge pull request #78 from mikix/model-desc
feat: support object-level descriptions
2026-01-14 16:14:35 -03:00
Michael Terry
2fd092cd8e feat: support enum-level descriptions
By saving them as a enum class docstring.
2026-01-14 14:01:02 -05:00
Michael Terry
e12396477f feat: support object-level descriptions
By saving them as a pydantic model docstring.

Fixes https://github.com/HideyoshiNakazone/jambo/issues/77
2026-01-14 13:14:48 -05:00
6440c30a91 Merge pull request #76 from HideyoshiNakazone/chore/adds-python3.14-metadata
chore: adds python3.14 metadata
2025-12-08 19:07:19 -03:00
19d1f72951 fix: minor fix in internal typing 2025-12-08 19:04:17 -03:00
02a28c9586 chore: updates uv version in ci 2025-12-08 18:56:55 -03:00
eee32a02ae chore: adds python3.14 to metadata 2025-12-08 18:56:01 -03:00
8 changed files with 31 additions and 4 deletions

View File

@@ -69,7 +69,7 @@ jobs:
uses: astral-sh/setup-uv@v5 uses: astral-sh/setup-uv@v5
with: with:
# Install a specific version of uv. # Install a specific version of uv.
version: "0.6.14" version: "0.9.15"
enable-cache: true enable-cache: true
cache-dependency-glob: "uv.lock" cache-dependency-glob: "uv.lock"

View File

@@ -36,6 +36,8 @@ class EnumTypeParser(GenericTypeParser):
# Create a new Enum type dynamically # Create a new Enum type dynamically
enum_type = Enum(name, {str(value).upper(): value for value in enum_values}) # type: ignore enum_type = Enum(name, {str(value).upper(): value for value in enum_values}) # type: ignore
enum_type.__doc__ = properties.get("description")
parsed_properties = self.mappings_properties_builder(properties, **kwargs) parsed_properties = self.mappings_properties_builder(properties, **kwargs)
if "default" in parsed_properties and parsed_properties["default"] is not None: if "default" in parsed_properties and parsed_properties["default"] is not None:

View File

@@ -22,6 +22,7 @@ class ObjectTypeParser(GenericTypeParser):
name, name,
properties.get("properties", {}), properties.get("properties", {}),
properties.get("required", []), properties.get("required", []),
description=properties.get("description"),
**kwargs, **kwargs,
) )
type_properties = self.mappings_properties_builder(properties, **kwargs) type_properties = self.mappings_properties_builder(properties, **kwargs)
@@ -48,6 +49,7 @@ class ObjectTypeParser(GenericTypeParser):
name: str, name: str,
properties: dict[str, JSONSchema], properties: dict[str, JSONSchema],
required_keys: list[str], required_keys: list[str],
description: str | None = None,
**kwargs: Unpack[TypeParserOptions], **kwargs: Unpack[TypeParserOptions],
) -> type[BaseModel]: ) -> type[BaseModel]:
""" """
@@ -74,7 +76,9 @@ class ObjectTypeParser(GenericTypeParser):
model_config = ConfigDict(validate_assignment=True) model_config = ConfigDict(validate_assignment=True)
fields = cls._parse_properties(name, properties, required_keys, **kwargs) fields = cls._parse_properties(name, properties, required_keys, **kwargs)
model = create_model(name, __config__=model_config, **fields) # type: ignore model = create_model(
name, __config__=model_config, __doc__=description, **fields
) # type: ignore
ref_cache[name] = model ref_cache[name] = model
return model return model

View File

@@ -5,7 +5,7 @@ from jambo.types import JSONSchema, RefCacheDict
from jsonschema.exceptions import SchemaError from jsonschema.exceptions import SchemaError
from jsonschema.validators import validator_for from jsonschema.validators import validator_for
from pydantic import BaseModel from pydantic import BaseModel
from typing_extensions import Optional from typing_extensions import MutableMapping, Optional
class SchemaConverter: class SchemaConverter:
@@ -17,8 +17,10 @@ class SchemaConverter:
fields and types. The generated model can be used for data validation and serialization. fields and types. The generated model can be used for data validation and serialization.
""" """
_namespace_registry: MutableMapping[str, RefCacheDict]
def __init__( def __init__(
self, namespace_registry: Optional[dict[str, RefCacheDict]] = None self, namespace_registry: Optional[MutableMapping[str, RefCacheDict]] = None
) -> None: ) -> None:
if namespace_registry is None: if namespace_registry is None:
namespace_registry = dict() namespace_registry = dict()
@@ -87,6 +89,7 @@ class SchemaConverter:
schema["title"], schema["title"],
schema.get("properties", {}), schema.get("properties", {}),
schema.get("required", []), schema.get("required", []),
description=schema.get("description"),
context=schema, context=schema,
ref_cache=ref_cache, ref_cache=ref_cache,
required=True, required=True,

View File

@@ -17,6 +17,7 @@ classifiers = [
"Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13", "Programming Language :: Python :: 3.13",
"Programming Language :: Python :: 3.14",
] ]
license = "MIT" license = "MIT"
readme = "README.md" readme = "README.md"

View File

@@ -49,6 +49,20 @@ class TestEnumTypeParser(TestCase):
) )
self.assertEqual(parsed_properties, {"default": None}) self.assertEqual(parsed_properties, {"default": None})
def test_enum_type_parser_creates_enum_with_description(self):
parser = EnumTypeParser()
schema = {
"description": "an enum",
"enum": ["value1"],
}
parsed_type, parsed_properties = parser.from_properties_impl(
"TestEnum",
schema,
)
self.assertEqual(parsed_type.__doc__, "an enum")
def test_enum_type_parser_creates_enum_with_default(self): def test_enum_type_parser_creates_enum_with_default(self):
parser = EnumTypeParser() parser = EnumTypeParser()

View File

@@ -24,6 +24,7 @@ class TestObjectTypeParser(TestCase):
properties = { properties = {
"type": "object", "type": "object",
"description": "obj desc",
"properties": { "properties": {
"name": {"type": "string"}, "name": {"type": "string"},
"age": {"type": "integer"}, "age": {"type": "integer"},
@@ -33,6 +34,7 @@ class TestObjectTypeParser(TestCase):
Model, _args = parser.from_properties_impl( Model, _args = parser.from_properties_impl(
"placeholder", properties, ref_cache={} "placeholder", properties, ref_cache={}
) )
self.assertEqual(Model.__doc__, "obj desc")
obj = Model(name="name", age=10) obj = Model(name="name", age=10)

View File

@@ -274,6 +274,7 @@ class TestSchemaConverter(TestCase):
} }
model = self.converter.build_with_cache(schema) model = self.converter.build_with_cache(schema)
self.assertEqual(model.__doc__, "A person")
obj = model(address={"street": "123 Main St", "city": "Springfield"}) obj = model(address={"street": "123 Main St", "city": "Springfield"})