Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make ProgressBar type available in public interface #2626

Open
ikokostya opened this issue Oct 19, 2023 · 1 comment
Open

Make ProgressBar type available in public interface #2626

ikokostya opened this issue Oct 19, 2023 · 1 comment

Comments

@ikokostya
Copy link

mypy considers function click.progressbar() as generic with type parameter V:

click/src/click/termui.py

Lines 287 to 303 in ca5e1c3

def progressbar(
iterable: cabc.Iterable[V] | None = None,
length: int | None = None,
label: str | None = None,
show_eta: bool = True,
show_percent: bool | None = None,
show_pos: bool = False,
item_show_func: t.Callable[[V | None], str | None] | None = None,
fill_char: str = "#",
empty_char: str = "-",
bar_template: str = "%(label)s [%(bar)s] %(info)s",
info_sep: str = " ",
width: int = 36,
file: t.TextIO | None = None,
color: bool | None = None,
update_min_steps: int = 1,
) -> ProgressBar[V]:

In most cases type parameter V will be inferred from iterable function parameter:

from typing import reveal_type

import click

with click.progressbar([1, 2, 3]) as bar:
    reveal_type(bar) # Revealed type is "click._termui_impl.ProgressBar[builtins.int]"

However, if iterable parameter is not specified (or if it's empty list), type parameter V cannot be inferred:

import click

with click.progressbar(length=1) as bar:
    pass
$ mypy mypy_click/__init__.py 
mypy_click/__init__.py:3: error: Need type annotation for "bar"  [var-annotated]

This issue can be solved with manually specified type parameter:

import click

with click.progressbar[int](length=1) as bar:
    pass

Unfortunately, this doesn't work for functions:

$ mypy mypy_click/
mypy_click/__init__.py:3: error: Type application is only supported for generic classes  [misc]

Another way to fix this is specify type for variable bar:

from typing import cast

import click
from click._termui_impl import ProgressBar

with cast(ProgressBar[int], click.progressbar(length=1)) as bar:
    pass

# or

with click.progressbar(length=1) as bar: # type: ProgressBar[int]
    pass

But in this case ProgressBar is imported as not a part of public interface.

So, I think ProgressBar type should be a part of the public interface. Maybe it should not
be exported directly like class, because users should use click.progressbar() function instead.
Public ProgressBar type can be defined as protocol and click.progressbar() just returns its implementation.

Environment:

  • Python version: 3.11.5
  • Click version: 8.1.7
  • Mypy version: 1.6.1
@ikokostya
Copy link
Author

Also possible to use function overloading to solve the problem above with inference of type parameter V:

@overload
def progressbar(iterable: Optional[Iterable[V]], ...) -> ProgressBar[V]: ...

@overload
def progressbar(length: int, ...) -> ProgressBar[int]: ...

Anyway, return type of the function should be public.

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

No branches or pull requests

1 participant