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

Use Annotated to add aliases to options #100

Open
richieadler opened this issue Jul 5, 2023 · 4 comments
Open

Use Annotated to add aliases to options #100

richieadler opened this issue Jul 5, 2023 · 4 comments

Comments

@richieadler
Copy link

The documentation gives an example of how to create aliases:

def echo(*text:Parameter.REQUIRED,
         prefix:'p'='', suffix:'s'='', reverse:'r'=False, repeat:'n'=1):
    ...

This, however, causes most linters to warn:

Unresolved reference 'p'

for all the aliases thus defined.

Since Python 3.9, typing.Annotated is available to add metadata to annotations, so this could be defined as

def echo(*text:Parameter.REQUIRED,
         prefix: Annotated[str, 'p'] ='', 
         suffix: Annotated[str, 's'] = '', 
         reverse: Annotated[str, 'r'] = False, 
         repeat: Annotated[int, 'n'] = 1,
):
    ...

and linters wouldn't complain.

@epsy
Copy link
Owner

epsy commented Jul 6, 2023

Hello!

Currently there is rudimentary support for using typing.Annotated in 5.0.0. It is not in the documentation, but there is an example that uses it: https://github.com/epsy/clize/blob/v5.0.0/examples/typed_cli.py

You have to wrap parts of the annotation meant for Clize with Clize[], for example your example would read:

def echo(*text:Parameter.REQUIRED,
         prefix: Annotated[str, Clize['p']] ='', 
         suffix: Annotated[str, Clize['s']] = '', 
         reverse: Annotated[bool, Clize['r']] = False, 
         repeat: Annotated[int, Clize['n']] = 1,
):
    ...

There's room to make this better, but this is a start. Let me know if you have feedback or ideas.

@richieadler
Copy link
Author

Is Clize needed? Can't just be a Literal?

@epsy
Copy link
Owner

epsy commented Jul 17, 2023

It is kind of needed for me to require it, yes.

See PEP 593 which introduces typings.Annotated, which says:

Ultimately, the responsibility of how to interpret the annotations (if at all) is the responsibility of the tool or library encountering the Annotated type. A tool or library encountering an Annotated type can scan through the annotations to determine if they are of interest (e.g., using isinstance()).

and then later:

Namespaces are not needed for annotations since the class used by the annotations acts as a namespace.

So it seems like the PEP expects library authors to distinguish different annotations by their type, so unfortunately str would be too ambiguous.

I hear you on this being very verbose. The PEP has a suggestion on the subject:

T = TypeVar('T')
Const = Annotated[T, my_annotations.CONST]

class C:
    def const_method(self: Const[List[int]]) -> int:

Perhaps in the future it could be applied like:

ValueType = TypeVar("ValueType")
ClizeAnnotationsT = TypeVar("ClizeAnnotationsT")
TypeAndClize = typings.Annotated[T, ClizeAnnotationsT]

def main(prefix: TypeAndClize[str, "p"]):
    ...

Though I think Clize may have to be updated to understand TypeVar/ClizeAnnotationsT

@richieadler
Copy link
Author

Though I think Clize may have to be updated to understand TypeVar/ClizeAnnotationsT

Agreed. And we may need to say something like

def main(a: str, b: Annotated[int, Parameter.REQUIRED]):
   ...

I don't know how to handle this case without Annotated...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
2 participants