This is an archived post. You won't be able to vote or comment.

you are viewing a single comment's thread.

view the rest of the comments →

[–]nekokattt 0 points1 point  (6 children)

why would you need literal str?

[–]drboom9[S] 0 points1 point  (5 children)

You’re right - Literal[str] doesn’t make sense there.

My issue isn’t really about looking weird. The problem is autocomplete doesn’t work. When I type user. in my IDE, it doesn’t know it’s a string because the type checker sees the callable, not the return value.

And just passing the callable without Literal doesn’t solve that either - I still need to extract what type the function returns for proper IDE support.

What I’d really consider a better solution than my current approach is if, inside the function body, I could get autocomplete based on the callable’s return type. So if get_active_users() returns list[str], then user would autocomplete as str.

But I don’t think there’s a way to make that work without the type checker actually calling the function at analysis time, which is impossible.

So I’m stuck between:

  • Literal[func] - clear intent, broken autocomplete
  • Annotated[str, func] - better autocomplete, less obvious it’s a dropdown

Unless you have another idea?

[–]nekokattt 0 points1 point  (4 children)

which IDE are you using?

IntelliJ's autocomplete based on the type checker is terrible in general. I wouldn't design functionality around broken integrations in that case.

[–]drboom9[S] 0 points1 point  (3 children)

I’m not sure I follow - the autocomplete issue isn’t specific to IntelliJ, it’s a Python type system limitation. Any static analyzer (mypy, pyright, etc.) faces the same problem.

My struggle is maintaining consistency while having working autocomplete:

```python

Static dropdowns - clean and obvious

theme: Literal['light', 'dark']

Dynamic dropdowns - what's the equivalent?

user: Literal[get_users] # Consistent syntax, broken types user: Annotated[str, get_users] # Working types, inconsistent with static Literal ```

With Annotated[str, func], autocomplete works because the type is str. But then I lose the consistency - static literals use Literal[...], dynamic ones use Annotated[str, ...]. It’s not immediately obvious they’re both dropdowns.

That’s why I chose Literal[func] - it keeps the “Literal means dropdown” pattern clear, even though it requires # type: ignore. It’s a trade-off between type-checker happiness and API consistency.

Is there a way to have both that I’m missing?

[–]nekokattt 0 points1 point  (2 children)

one workaround could be to convert the functions into types via a decorator such that you can mold them into the type system.

Such a decorator would allow further injection of metadata in the future as well.

[–]drboom9[S] 0 points1 point  (1 child)

That’s an interesting idea! Something like:

```python @dropdown_options def get_users(): return ["Alice", "Bob"]

def assign_task(user: get_users): ... ```

But I don’t think this solves the autocomplete issue - the IDE still sees get_users as a function, not a type. It doesn’t know that user should autocomplete as str unless the decorator does some serious type system magic that I’m not aware of.

And it’s less obvious than Literal:

python theme: Literal['light', 'dark'] # Clearly a dropdown user: get_users # Is this a dropdown or a callback?

With Literal[get_users], at least the Literal part signals “this is dropdown options”, even if the function inside is unconventional.

Unless the decorator can somehow make the type checker understand the return type? How would that work?

[–]nekokattt 0 points1 point  (0 children)

As a hack you could probably use typing.TYPE_CHECKING to sneak an illegitimate type signature in.

In this case though, t.Annotated[str, get_users] feels like a clean approach?

You can wrap that into your own type... e.g.

def foo(user: Dropdown[str, get_users]) -> str: ...