Skip to content

fix: serialize and deserialize typing.Literal - #12286

Merged
sjrl merged 2 commits into
deepset-ai:mainfrom
LK-maker-007:fix-serialize-literal
Aug 10, 2026
Merged

fix: serialize and deserialize typing.Literal#12286
sjrl merged 2 commits into
deepset-ai:mainfrom
LK-maker-007:fix-serialize-literal

Conversation

@LK-maker-007

Copy link
Copy Markdown
Contributor

Related Issues

Proposed Changes:

serialize_type had no handling for typing.Literal, so it fell through to the generic-path logic and rendered the literal's values as if they were type names:

serialize_type(Literal["yes", "no"])   # -> "typing.Literal[yes, no]"

deserialize_type then tried to resolve yes and no as types and raised DeserializationError. A value that happened to look like a real type name silently corrupted instead of raising: Literal["int", "str"] round-tripped to Literal[int, str] (two types).

The fix special-cases typing.Literal on both sides:

  • serialize: render each value with repr(), so strings keep their quotes and non-string values (int, bool, bytes, None) are emitted as valid Python literals.
  • deserialize: parse the arguments with ast.literal_eval instead of the type-resolving generic path. ast.literal_eval is quote-aware, so a comma inside a string value (Literal["a, b", "c"]) no longer splits the arguments, and it is limited to safe literals (str/int/bool/None/bytes/tuples).

Scope note: this covers the literal value kinds Python's own Literal accepts and that survive a text round-trip (str, bytes, int, bool, None). Enum members as Literal values are out of scope — they can't be reconstructed from repr() alone and would need import-path handling; I left that out rather than half-support it. Happy to follow up if you want it.

How did you test it?

Unit tests in test/utils/test_type_serialization.py (serialization, deserialization, and round_trip for Literal, mirroring the existing Callable trio), covering the failing case, the silent-corruption case (Literal["int", "str"]), non-string values, a comma inside a value, and Literal nested in Optional/Union. Proven fail-without/pass-with: with the source change stashed the three new tests fail with DeserializationError; with it applied they pass.

Full file green (195 passed), plus the consumer suites: test_output_adapter.py, test_conditional_router.py, test_pipeline.py (80 passed). ruff check / ruff format --check clean, mypy clean on the changed module.

End-to-end check — this previously crashed on load, now round-trips:

p = Pipeline()
p.add_component("oa", OutputAdapter(template="{{ x }}", output_type=Literal["yes", "no"]))
Pipeline.loads(p.dumps())   # OK

Notes for the reviewer

The Literal branch is placed before the generic-args parsing on both sides on purpose: typing.get_origin(Literal[...]) is typing.Literal, and its args are values, so they must not go through the type-resolving path (_parse_generic_args / deserialize_type per arg).

Checklist

serialize_type rendered a Literal's values as bare tokens (e.g.
typing.Literal[yes, no]), which deserialize_type then tried to resolve
as types and failed on; values that looked like type names such as
Literal["int", "str"] were silently converted to types on the round
trip. Serialize each value with repr() and read it back with
ast.literal_eval so the values (including strings, and commas inside a
string) round-trip through pipeline serialization.

Fixes deepset-ai#12285
@LK-maker-007
LK-maker-007 requested a review from a team as a code owner August 10, 2026 02:10
@LK-maker-007
LK-maker-007 requested review from anakin87 and removed request for a team August 10, 2026 02:10
@vercel

vercel Bot commented Aug 10, 2026

Copy link
Copy Markdown

@LK-maker-007 is attempting to deploy a commit to the deepset Team on Vercel.

A member of the Team first needs to authorize it.

@sjrl

sjrl commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

@anakin87 I can take review of this one

@sjrl
sjrl requested review from sjrl and removed request for anakin87 August 10, 2026 05:50
Comment thread haystack/utils/type_serialization.py Outdated
Comment thread haystack/utils/type_serialization.py Outdated
Comment thread releasenotes/notes/fix-serialize-literal-0b79f23f2a1ca883.yaml Outdated
Comment thread test/utils/test_type_serialization.py Outdated
@github-actions

github-actions Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Coverage report

Click to see where and how coverage changed

FileStatementsMissingCoverageCoverage
(new stmts)
Lines missing
  haystack/utils
  type_serialization.py
Project Total  

This report was generated by python-coverage-comment-action

@LK-maker-007
LK-maker-007 requested a review from sjrl August 10, 2026 11:50
@LK-maker-007

Copy link
Copy Markdown
Contributor Author

Applied all four: trimmed both comments, removed the test comment, and switched the release note to RST double backticks throughout (not just the flagged line). Thanks for the review.

@github-actions github-actions Bot added the type:documentation Improvements on the docs label Aug 10, 2026

@sjrl sjrl 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.

Thanks!

@sjrl
sjrl merged commit 50eebe0 into deepset-ai:main Aug 10, 2026
23 of 24 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

topic:tests type:documentation Improvements on the docs

Projects

None yet

Development

Successfully merging this pull request may close these issues.

serialize_type / deserialize_type do not support typing.Literal

2 participants