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

Failure to use typing.NewType as the type for an option #2773

Open
mthorpe7 opened this issue Sep 5, 2024 · 1 comment
Open

Failure to use typing.NewType as the type for an option #2773

mthorpe7 opened this issue Sep 5, 2024 · 1 comment

Comments

@mthorpe7
Copy link

mthorpe7 commented Sep 5, 2024

typing.NewType is a cheap way to create an alias type for type checking only (at runtime it is the underlying type). I was hoping to use this one in a @click.option parameter/type list, but this does something unexpected (although I can understand why it behaves like this).

from typing import NewType, reveal_type
import click

UserId = NewType("UserId", int)

@click.command
@click.option("--user-id", type=UserId, default=UserId(1))
def main(user_id: UserId):
    reveal_type(UserId)
    reveal_type(user_id)
    assert isinstance(user_id, int)

if __name__ == "__main__":
    main()

With the default, this is happy:

$ python cli.py 
Runtime type is 'NewType'
Runtime type is 'int'

when passing a value, it isn't:

$ python cli.py --user-id 10
Runtime type is 'NewType'
Runtime type is 'str'
Traceback (most recent call last):
  File "/work/cli.py", line 17, in <module>
    main()
  File "/work/.venv/lib/python3.12/site-packages/click/core.py", line 1157, in __call__
    return self.main(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/work/.venv/lib/python3.12/site-packages/click/core.py", line 1078, in main
    rv = self.invoke(ctx)
         ^^^^^^^^^^^^^^^^
  File "/work/.venv/lib/python3.12/site-packages/click/core.py", line 1434, in invoke
    return ctx.invoke(self.callback, **ctx.params)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/work/.venv/lib/python3.12/site-packages/click/core.py", line 783, in invoke
    return __callback(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/work/cli.py", line 13, in main
    assert isinstance(user_id, int)
           ^^^^^^^^^^^^^^^^^^^^^^^^
AssertionError

Environment:

  • Python version: Python 3.12.5
  • Click version: click-8.1.7
@davidism
Copy link
Member

davidism commented Sep 5, 2024

type is the Click type, not the Python type. You'd pass click.Integer (or int as a hard-coded shortcut) there. If you know of a way to introspect that something is a NewType at runtime, and find out what it aliases (int) then that can be added to the automatic conversions. But in general, you're expecting a level of typing support that we just don't do.

click/src/click/types.py

Lines 1001 to 1058 in 4a758f5

def convert_type(ty: t.Any | None, default: t.Any | None = None) -> ParamType:
"""Find the most appropriate :class:`ParamType` for the given Python
type. If the type isn't provided, it can be inferred from a default
value.
"""
guessed_type = False
if ty is None and default is not None:
if isinstance(default, (tuple, list)):
# If the default is empty, ty will remain None and will
# return STRING.
if default:
item = default[0]
# A tuple of tuples needs to detect the inner types.
# Can't call convert recursively because that would
# incorrectly unwind the tuple to a single type.
if isinstance(item, (tuple, list)):
ty = tuple(map(type, item))
else:
ty = type(item)
else:
ty = type(default)
guessed_type = True
if isinstance(ty, tuple):
return Tuple(ty)
if isinstance(ty, ParamType):
return ty
if ty is str or ty is None:
return STRING
if ty is int:
return INT
if ty is float:
return FLOAT
if ty is bool:
return BOOL
if guessed_type:
return STRING
if __debug__:
try:
if issubclass(ty, ParamType):
raise AssertionError(
f"Attempted to use an uninstantiated parameter type ({ty})."
)
except TypeError:
# ty is an instance (correct), so issubclass fails.
pass
return FuncParamType(ty)

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

2 participants