Skip to content

fix: render empty-tuple subscript annotation as tuple[()] not tuple[]#474

Open
gaoflow wants to merge 1 commit into
mkdocstrings:mainfrom
gaoflow:fix-empty-tuple-annotation-str
Open

fix: render empty-tuple subscript annotation as tuple[()] not tuple[]#474
gaoflow wants to merge 1 commit into
mkdocstrings:mainfrom
gaoflow:fix-empty-tuple-annotation-str

Conversation

@gaoflow

@gaoflow gaoflow commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

For reviewers

  • I did not use AI
  • I used AI and thoroughly reviewed every code/docs change

Description of the change

Fix rendering for empty-tuple subscript annotations so tuple[()] and Tuple[()] stringify as valid Python instead of tuple[] / Tuple[].

tuple[()] is a valid annotation for a zero-element tuple. Griffe parses it as an ExprSubscript whose slice is an implicit, empty ExprTuple. Because ExprTuple.iterate() suppressed parentheses when implicit=True and yielded no elements for an empty tuple, the outer subscript rendered empty brackets.

An empty tuple has no omitted source form; it must be written as (). This adds a focused early return for the empty implicit tuple case and covers both builtin tuple and typing.Tuple with test_empty_tuple_annotation_str.

Validation:

  • Added test_empty_tuple_annotation_str.
  • All 977 tests pass locally.

Relevant resources

  • No linked issue.

An empty `ExprTuple` with `implicit=True` (produced when parsing a
subscript like `tuple[()]`) previously yielded nothing in `iterate()`,
resulting in the invalid string `tuple[]`.  An empty tuple has no
implicit form—it must always be written as `()`—so treat an empty
implicit tuple the same as an explicit one.

Before: `str(returns)` of `def f() -> tuple[()]` gave `"tuple[]"`,
which is a `SyntaxError` when re-parsed.

After: correctly yields `"tuple[()]"`.

@pawamoy pawamoy left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks.

Please follow our PR template.

"""Whether the tuple is implicit (e.g. without parentheses in a subscript's slice)."""

def iterate(self, *, flat: bool = True) -> Iterator[str | Expr]:
if self.implicit and not self.elements:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

An empty tuple is always written as ()

So we don't need to check self.implicit, right?

@gaoflow

gaoflow commented Jun 26, 2026

Copy link
Copy Markdown
Contributor Author

Updated the PR description to follow the template, including the AI disclosure checkbox.

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.

2 participants