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

Add type hints to variables.py and platforms.py #1042

Merged
merged 4 commits into from
Sep 25, 2020

Conversation

Eric-Arellano
Copy link
Contributor

Unfortunately, so long as we require Python 2, we cannot use namedtuple robustly with MyPy because it does not support annotating the fields of a named tuple. So, we recreate namedtuple through our own conventional class. This can be fixed once we require Python 3 to run. See #1041.



def requires_python(dist):
# type: (Distribution) -> Optional[SpecifierSet]
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Note that MyPy doesn't actually know what these are due to the vendoring. But I believe that it will at least check we are using type A, rather than type Z; it just doesn't understand the methods/fields on the type.

And this helpful for the reader.

Copy link
Contributor

Choose a reason for hiding this comment

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

We can possibly create stubs for these if needed?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oh, that'd be interesting as a followup once we have higher coverage. pkg_resources is pretty key to Pex. Having type hints working with Pex would be great.


class PEXWarning(Warning):
"""Indicates a warning from PEX about suspect buildtime or runtime configuration."""


def configure_warnings(pex_info, env):
if env.PEX_VERBOSE > 0:
# type: (PexInfo, Variables) -> None
if env.PEX_VERBOSE is not None and env.PEX_VERBOSE > 0:
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This was a bug, although I don't know if we ever hit it. It's possible for PEX_VERBOSE to be None, such as if you call ENV.strip() and the user did not define the value.

Copy link
Member

Choose a reason for hiding this comment

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

There is a bug lurking but its unhittable:
https://github.com/pantsbuild/pex/blob/aae2a6ad55e148ada43f36c1d486af9b2312af50/pex/variables.py#L364-L372
https://github.com/pantsbuild/pex/blob/aae2a6ad55e148ada43f36c1d486af9b2312af50/pex/variables.py#L104-L113

... combined with use_defaults is True for the global ENV which is what the only use of configure_warnings in production passes (all of production only uses the global ENV).

pex/platforms.py Outdated

def __init__(self, platform, impl, version, abi):
super(Platform, self).__init__()
if not all((platform, impl, version, abi)):
Copy link
Contributor Author

Choose a reason for hiding this comment

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

It'd be better to check if these are str, rather than simply being truthy. But I tried to minimize my changes. Lmk if I should add it.

Copy link
Contributor

Choose a reason for hiding this comment

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

I think we might want to make abi at least into an enum when we can drop py2, although I'm not sure checking for str now makes it harder to do later. I think leaving it as you've got it here sounds good.

pex/platforms.py Outdated Show resolved Hide resolved
self._use_defaults = use_defaults
self._environ = (environ if environ is not None else os.environ).copy()
self._environ = environ.copy() if environ is not None else os.environ.copy()
Copy link
Contributor Author

Choose a reason for hiding this comment

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

MyPy was getting confused trying to find the common denominator between our Dict vs. the _OSEnviron type, then complaining that .copy() wasn't define on that common interface of MutableMapping. This fixes it.

else:
return self._defaulted(default)
if value is None:
return self._defaulted(default) # type: ignore[return-value]
Copy link
Contributor Author

Choose a reason for hiding this comment

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

We have to ignore due to the imprecise ._defaulted method.

Copy link
Contributor

Choose a reason for hiding this comment

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

Might this be the place for a cast()?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We can't because it returns Optional[bool] :/

Copy link
Contributor

Choose a reason for hiding this comment

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

That's quite annoying.

Copy link
Member

Choose a reason for hiding this comment

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

Noted in #1042, but you can use cast, just pass the types as strings when those types (or aliases) are only available at TYPE_CHECKING time.

value = self._environ.get(variable)
if value is not None:
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This method is the same code as before, only refactored by inverting the conditional and early returns.

Comment on lines +265 to +271
# type: () -> Optional[bool]
"""Boolean.

Enable application profiling. If specified and PEX_PROFILE_FILENAME is not specified, PEX
will print profiling information to stdout.
"""
return self._get_path("PEX_PROFILE", default=None)
return self._get_bool("PEX_PROFILE", default=False)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This was confusing to me. I think a bug. We document it as a bool, but treat it as a path. @jsirois lmk if you know what we intended.

Copy link
Member

Choose a reason for hiding this comment

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

LGTM.

I don't know but git does. See 0fd58f3 via git log -S PEX_PROFILE_FILENAME.

It looks like an oversight in that change to switch from path to bool when PEX_PROFILE_FILENAME was introduced / split out to parallel PEX_COVERAGE*.

pex/platforms.py Outdated Show resolved Hide resolved


def requires_python(dist):
# type: (Distribution) -> Optional[SpecifierSet]
Copy link
Contributor

Choose a reason for hiding this comment

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

We can possibly create stubs for these if needed?

@@ -21,15 +25,17 @@ class Variables(object):

@classmethod
def process_pydoc(cls, pydoc):
# type: (Optional[str]) -> Tuple[str, str]
if pydoc is None:
Copy link
Contributor

Choose a reason for hiding this comment

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

Possibly consider extending this to extract type hints in the future.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Once we require Python 3, that would be super easy to do with typing.get_type_hints(), which is how the Pants rule signature parsing code works.


class PEXWarning(Warning):
"""Indicates a warning from PEX about suspect buildtime or runtime configuration."""


def configure_warnings(pex_info, env):
if env.PEX_VERBOSE > 0:
# type: (PexInfo, Variables) -> None
if env.PEX_VERBOSE is not None and env.PEX_VERBOSE > 0:
Copy link
Member

Choose a reason for hiding this comment

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

There is a bug lurking but its unhittable:
https://github.com/pantsbuild/pex/blob/aae2a6ad55e148ada43f36c1d486af9b2312af50/pex/variables.py#L364-L372
https://github.com/pantsbuild/pex/blob/aae2a6ad55e148ada43f36c1d486af9b2312af50/pex/variables.py#L104-L113

... combined with use_defaults is True for the global ENV which is what the only use of configure_warnings in production passes (all of production only uses the global ENV).

pex/platforms.py Outdated Show resolved Hide resolved
Comment on lines +265 to +271
# type: () -> Optional[bool]
"""Boolean.

Enable application profiling. If specified and PEX_PROFILE_FILENAME is not specified, PEX
will print profiling information to stdout.
"""
return self._get_path("PEX_PROFILE", default=None)
return self._get_bool("PEX_PROFILE", default=False)
Copy link
Member

Choose a reason for hiding this comment

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

LGTM.

I don't know but git does. See 0fd58f3 via git log -S PEX_PROFILE_FILENAME.

It looks like an oversight in that change to switch from path to bool when PEX_PROFILE_FILENAME was introduced / split out to parallel PEX_COVERAGE*.

We don't fix IntegResults, though. That's test only, and we don't seem to need any of the generated dunders. It would be lots of boilerplate to go back. In contrast, `Platform` is used in production so worth the boilerplate.
pex/platforms.py Outdated Show resolved Hide resolved
@Eric-Arellano Eric-Arellano merged commit b921723 into pex-tool:master Sep 25, 2020
@Eric-Arellano Eric-Arellano deleted the type-hints branch September 25, 2020 21:38
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.

None yet

3 participants