Skip to content
Open
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
19 changes: 19 additions & 0 deletions python/pyfory/format/tests/test_infer.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,25 @@ class X:
assert result.type.id == TypeId.STRUCT


def test_infer_field_builtin_types_not_treated_as_struct():
"""Built-in types must NOT be routed to visit_customized (regression guard)."""
assert _infer_field("", int).type.id == TypeId.INT64
assert _infer_field("", float).type.id == TypeId.FLOAT64
assert _infer_field("", str).type.id == TypeId.STRING
assert _infer_field("", bytes).type.id == TypeId.BINARY
assert _infer_field("", bool).type.id == TypeId.BOOL


def test_infer_field_nested_custom_class():
"""Custom class nested inside a List should also be handled correctly."""

class Inner:
pass

result = _infer_field("", List[Inner])
assert result.type.id == TypeId.LIST


def test_infer_class_schema():
schema = infer_schema(Foo)
assert schema.num_fields == 7
Expand Down
10 changes: 8 additions & 2 deletions python/pyfory/type_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -249,11 +249,17 @@ def infer_field(field_name, type_, visitor: TypeVisitor, types_path=None):
else:
raise TypeError(f"Collection types should be {list, dict} instead of {type_}")
else:
if is_function(origin) or not hasattr(origin, "__annotations__"):
if is_function(origin):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So I do not understand, what does this fix exactly?

Copy link
Author

@SURYAS1306 SURYAS1306 Feb 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Previously, types without __annotations__ (including non-dataclass classes) were being routed through the same branch as functions due to the or not hasattr(origin, "__annotations__") condition.

This caused incorrect type dispatch for custom classes.

By removing the not hasattr(origin, "__annotations__") check, only actual functions are handled in that branch, and class types now follow the intended dispatch path.

Please let me know if I should clarify this further in the PR description.

return visitor.visit_other(field_name, type_, types_path=types_path)
else:

if origin in (list, dict, set):
return visitor.visit_other(field_name, type_, types_path=types_path)

if inspect.isclass(origin) and origin.__module__ not in ("builtins", "datetime"):
return visitor.visit_customized(field_name, type_, types_path=types_path)

return visitor.visit_other(field_name, type_, types_path=types_path)


def is_function(func):
return inspect.isfunction(func) or is_cython_function(func)
Expand Down
Loading