Skip to content

fix(types): don't treat instance methods on Enum output types as choices#1916

Merged
RobinPicard merged 1 commit into
dottxt-ai:mainfrom
ErenAta16:fix/enum-instance-method-not-a-choice
Jul 20, 2026
Merged

fix(types): don't treat instance methods on Enum output types as choices#1916
RobinPicard merged 1 commit into
dottxt-ai:mainfrom
ErenAta16:fix/enum-instance-method-not-a-choice

Conversation

@ErenAta16

Copy link
Copy Markdown
Contributor

Fixes #1915.

_get_enum_members scans an Enum class's __dict__ for plain FunctionType values to support enums whose members are assigned as bare functions (e.g. c = some_function), since Python's Enum machinery treats a plain function assigned in the class body as a method rather than a member - it doesn't show up through normal iteration.

The scan had no way to tell that pattern apart from an actual instance method defined with def directly in the class body (e.g. describe(self)). Both end up as plain FunctionType objects in __dict__, so any Enum used as an output type that happened to also define a helper method crashed:

from enum import Enum
from outlines.types.dsl import python_types_to_terms

class Color(Enum):
    RED = "red"
    GREEN = "green"
    BLUE = "blue"

    def describe(self) -> str:
        return f"This color is {self.value}"

python_types_to_terms(Color)
# ValueError: Each argument must have a type annotation

describe gets swept up as a "member," and building a JSON schema from its signature fails on the unannotated self - a misleading error unrelated to the actual problem.

Fix

__qualname__ distinguishes the two cases: a method defined directly in the class body is qualified with the enclosing class's own name ("Color.describe"), while a function defined elsewhere and merely assigned as a member's value keeps its original qualname ("some_function"). Filtering on __qualname__ excludes real instance methods while still picking up the bare-function-member pattern that test_dsl_python_types_to_terms's SomeEnum.c = func case already covers.

I initially proposed restricting the scan to functools.partial values only (mentioned in the issue), but that turned out to be too narrow - it broke the existing SomeEnum.c = func test, which assigns a plain function directly without partial. Left a correction comment on the issue with the details; the __qualname__ approach here preserves that pattern.

Testing

Added test_dsl_enum_with_instance_method_is_not_a_choice to tests/types/test_dsl.py, covering the crash case from the issue.

Ran the full tests/types/ suite locally (233 tests, all passing), plus tests/types/test_dsl.py and tests/types/test_types_utils.py specifically to make sure the existing SomeEnum/enum-related coverage still passes unchanged.

@github-actions

Copy link
Copy Markdown

📚 Documentation preview: https://dottxt-ai.github.io/outlines/pr-preview/pr-1916/

Preview updates automatically with each commit.

@RobinPicard RobinPicard left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Good catch, thanks!

_get_enum_members scans an Enum class's __dict__ for plain FunctionType
values to support enums whose members are functions assigned directly
(e.g. `c = some_function`), since Python's Enum machinery treats those
as methods rather than members under normal iteration.

The scan had no way to tell that apart from an ordinary instance method
defined with def directly in the class body (e.g. describe(self)) - both
end up as plain FunctionType objects in __dict__. Any Enum with a helper
method crashed in python_types_to_terms with a misleading error about
missing type annotations, because the method's self parameter has none.

Use __qualname__ to distinguish the two: a method defined in the class
body is qualified with the class's own name ("Color.describe"), while a
function defined elsewhere and assigned as a member's value keeps its
original qualname ("some_function"). This excludes actual instance
methods while preserving the existing bare-function-member pattern
already covered by test_dsl_python_types_to_terms's SomeEnum.c case.

Fixes dottxt-ai#1915
@RobinPicard
RobinPicard force-pushed the fix/enum-instance-method-not-a-choice branch from c0994c3 to af28053 Compare July 20, 2026 09:00
@RobinPicard
RobinPicard merged commit 39f8542 into dottxt-ai:main Jul 20, 2026
5 of 6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Enum output types: any plain instance method on the class gets treated as a generation choice

2 participants