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 NormalizedName visible at runtime #329

Merged
merged 2 commits into from
Aug 6, 2020

Conversation

sbidoul
Copy link
Member

@sbidoul sbidoul commented Aug 2, 2020

I'm writing a python 3 program and I'd like to use the canonicalize_name function and it's return type NormalizedName type. It seems I can't because the type is hidden behind the TYPE_CHECKING guard and is therefore not available at runtime. This mean I cannot currently write this:

from packaging.utils import NormalizedName

def my_function(name: NormalizedName) -> None:
    ...

This PR is an attempt to work around this.

@brettcannon
Copy link
Member

What version of Python is this for? Can you use from __future__ import annotations?

@pradyunsg
Copy link
Member

The intended usage (when I wrote this anyway) was to have the import happen under an if TYPE_CHECKING and the usage to never involve direct referencing (eg: 'NormalizedName' or in a # type: comment).

That said, I think this is a perfectly reasonable change, that would also keep working when we change this bit of code to drop Python 2 support.

@sbidoul
Copy link
Member Author

sbidoul commented Aug 6, 2020

Yes my expectation is that return types of public functions are importable. The TYPE_CHECKING guard is fine when running mypy but, unless I'm missing something, types defined under that guard are not usable in type annotations, only in # type: ... comments.

@pradyunsg
Copy link
Member

pradyunsg commented Aug 6, 2020

types defined under that guard are not usable in type annotations

They are usable. As for how, it'll be easier to convey w/ code examples...

Status quo:

from mypy import TYPE_CHECKING

if TYPE_CHECKING:
    from packaging.utils import NormalizedName


# notice the quotes here -- it's a string.
def my_awesome_function(name: "NormalizedName") -> None:
    print(name)


def another_awesome_function(name):
    # type: (NormalizedName) -> None
    print(name)

With this PR:

from packaging.utils import NormalizedName


# you can still put quotes here, but you don't need to.
def my_awesome_function(name: NormalizedName) -> None:
    print(name)


def another_awesome_function(name):
    # type: (NormalizedName) -> None
    print(name)

I think it's a worthwhile simplification and, well, once we drop Python 2, we can start using typing directly (since pip can import things from the standard library safely).

@sbidoul
Copy link
Member Author

sbidoul commented Aug 6, 2020

Oh, TIL that we can use strings in type annotations, thanks. I will not want to abuse that though.

@brettcannon
Copy link
Member

And to be clear, from __future__ import annotations is the equivalent of using strings for type annotations everywhere.

@pradyunsg
Copy link
Member

@brettcannon but that isn't enough because we need to still import behind the conditional to avoid actually running code (imagine I said this in playful tone).

I'm gonna merge this in because no one is opposed to it. :)

@pradyunsg pradyunsg merged commit eca30e0 into pypa:master Aug 6, 2020
@sbidoul sbidoul deleted the normalized_name-sbi branch August 6, 2020 17:28
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

Successfully merging this pull request may close these issues.

3 participants