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

Force type hints #233

Merged

Conversation

finswimmer
Copy link
Member

This PR add missing type hints to both package code and test code. Furthermore flake-annotations is added as a flake8 plugin to enforce type hints in the future.

@finswimmer finswimmer marked this pull request as ready for review November 15, 2021 06:30
@finswimmer finswimmer requested a review from a team November 15, 2021 06:31
@@ -1,7 +1,11 @@
[flake8]
Copy link
Member

@neersighted neersighted Nov 15, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This whole file is very cargo-culted. Can we clean it up while we're in here?

.flake8 Show resolved Hide resolved
.pre-commit-config.yaml Outdated Show resolved Hide resolved
@@ -46,7 +47,21 @@ def __post_init__(self):

object.__setattr__(self, "_compare_key", self._make_compare_key())

def _make_compare_key(self):
def _make_compare_key(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wow, this is a complicated type. Is there any way to clean this up with NewType, a refactor with an object, or similar?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was happy, that PyCharm was doing this for me 😆 IMO refactoring should be done in another PR, because the purpose of this PR is to enforce type hints in the future.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is a brutal type signature :)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've tried to understand the code and IMO the signature can be written more easily. See code change.

Copy link
Contributor

@danieleades danieleades left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mypy itself can be configured to return errors on missing annotations, i believe. is the 'flake8' plugin somehow preferable?

  • if TYPE_CHECKING pattern doesn't appear to be required throughout
  • inconsistent use of dict/Dict

@@ -10,7 +11,12 @@
from tests.testutils import tempfile


def pytest_addoption(parser):
if TYPE_CHECKING:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you shouldn't need the if TYPE_CHECKING pattern here- that's really only for preventing circular imports. You can just import these directly, and use them below without quoting the types

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a convention I'm used to. If you need a module just for type hints, import it only in the if TYPE_CHECKING block.

It's easier to teach to other people ("Why are you sometime put import in that block and sometimes not?") and the overall chance that you have a circular import if you need this import just for type hint is quite big.

TL;DR: I prefer to leave it as it is.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fair enough. there's some interesting background on it here - larryhastings/co_annotations#1

in general, it seems that if TYPE_CHECKING is used iff

  • you would otherwise have a circular import
  • the import is 'expensive'

the Benevolent Dictator for Life seems to regard it is a painfully necessary hack.

that said, it's also clear from the thread that it's far from settled. And using it everywhere instead of the small number of cases where you need it (do you definitely need it anywhere in this codebase?) is at least consistent.

@@ -20,15 +26,15 @@ def pytest_addoption(parser):
)


def pytest_configure(config):
def pytest_configure(config: "Config") -> None:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can use Config unquoted if you remove the if TYPE_CHECKING pattern above

@@ -4,7 +4,7 @@


@pytest.fixture
def base_object():
def base_object() -> dict:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this syntax is python 3.9+. less than that, the correct return type would be Dict, rather than dict.

I guess we can get away with it, since mypy is being run with a recent interpreter, and the type is ignored during other testing, but whichever way we go, it should be consistent throughout the codebase

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

while we're at it, these dict annotations are equivalent to Dict[Any, Any]- we should be able to do better than that

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As you find out dict is valid as well, but don't let us define how keys and values look like in python<3.9. I agree it's in inconsistent use if we use Dict in other places, so I changed it.

while we're at it, these dict annotations are equivalent to Dict[Any, Any]- we should be able to do better than that

The only way to do it better here is to use Dict[str, Any] (which doesn't add much benefit) or use TypedDict. But this is only available for python >=3.8 and would require to add typing-extensions as dependency for python <3.8. Not sure if we want to introduce another dependency.

@@ -10,7 +13,11 @@
from poetry.core.utils._compat import PY37


def test_builder_find_excluded_files(mocker):
if TYPE_CHECKING:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is there a circular import if you do not guard the import of MockerFixture?

Comment on lines +64 to +66
def parse(
cls, value: str, version_class: Optional[Type["PEP440Version"]] = None
) -> "PEP440Version":
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if you remove the if TYPE_CHECKING guard, this can be written as-

Suggested change
def parse(
cls, value: str, version_class: Optional[Type["PEP440Version"]] = None
) -> "PEP440Version":
def parse(
cls, value: str, version_class: Optional[PEP440Version] = None
) -> PEP440Version:

@finswimmer finswimmer force-pushed the improvement/force-type-hints branch 2 times, most recently from a8847b5 to de16fac Compare November 15, 2021 13:06
@danieleades
Copy link
Contributor

might be a good idea to merge #199 before this is merged, in order to catch any type errors that could be interested by this PR

@neersighted
Copy link
Member

neersighted commented Nov 15, 2021

re: if TYPE_CHECKING, check out https://github.com/asottile/flake8-typing-imports

Looks like a useful set of lints we could apply here.

@finswimmer finswimmer merged commit 4d119fd into python-poetry:master Nov 16, 2021
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