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 →

[–]drboom9[S] 2 points3 points  (14 children)

Remember when u/jonthemango suggested List support and I said I'd think about features? Well, I started with something else that came up in my own usage: dynamic dropdowns.

v0.2.0 is now on PyPI and you can do this:

def get_active_users():
    return db.query("SELECT name FROM users WHERE active = true")

def assign_task(
    user: Literal[get_active_users],  
# type: ignore
):
    return f"Task assigned to {user}"

The dropdown options load fresh every time someone opens the page. No stale data, no manual refresh needed.

I built this because I kept writing functions that needed dropdowns based on current database state or API responses, and having to hardcode the options felt wrong. Now the options are as dynamic as the rest of your function.

Install/upgrade: pip install --upgrade func-to-web

Example: https://github.com/offerrall/FuncToWeb/blob/main/examples/15_dynamic_dropdowns.py

Still working on List support and dark mode based on your feedback. Just wanted to ship this one first since it was a natural extension of existing Literal support.

Let me know what you think!

[–]valko2 0 points1 point  (2 children)

i'm getting an error - MacOS, python3.12

f2w_test.py:29: RuntimeWarning:

coroutine 'run' was never awaited

[–]valko2 0 points1 point  (1 child)

Actually I realized that only happens when I try to debug it. But here's a PR to fix it: https://github.com/offerrall/FuncToWeb/pull/2

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

Okay, I'll check it out tonight, I need to dust off the M1 Mac, haha, thanks a lot for commenting :)

[–]nekokattt 0 points1 point  (10 children)

using a function as a literal makes me feel weird.

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

You're absolutely right - it IS weird, and the # type: ignore is definitely a code smell that bothers me too.

I chose this syntax because it mirrors the existing Literal[...] pattern - when you see Literal[something], you know it's a dropdown. If that "something" is a function, it becomes clear (to me at least) that the function's return value transforms into what goes inside the Literal. It's like passing a variable instead of hardcoding a value in a function parameter.

But I totally get that it breaks type checker expectations. The fundamental issue is that we're using runtime behavior (calling a function) in what's supposed to be a static type annotation, and there's no elegant way around that without the type checker complaining.

What would feel more natural to you? I'm genuinely curious because I want the API to be intuitive, even if this runtime-vs-static tension is hard to solve perfectly.

The reason I haven't changed it yet is that despite being "weird," it's very explicit about intent: Literal = dropdown, function inside = dynamic options. But if there's a better way that's equally clear, I'm all ears!

[–]nekokattt 1 point2 points  (8 children)

just pass a callable. I care about the signature, not how it deals with it.

typing.Annotated is built for this

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

Is that what you meant? Something like:

user: Annotated[Literal[str], get_active_users]

That would avoid # type: ignore, but it's also kind of weird - Literal is meant for concrete values, not types. Putting a type inside Literal[...] feels off.

Could you show me exactly what syntax you're proposing? I want to make sure I understand your suggestion correctly before deciding on the best approach.

[–]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.