Skip to content

Overload the constructor of subprocess.Popen #3113

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

Merged
merged 2 commits into from
Jul 16, 2019
Merged

Conversation

msullivan
Copy link
Contributor

This takes advantage of a recent mypy change to respect the return
type of __new__. Using that it does the same tedious overloads
as run and check_output.

This takes advantage of a recent mypy change to respect the return
type of `__new__`. Using that it does the same tedious overloads
as `run` and `check_output`.
@msullivan msullivan requested review from JukkaL and ilevkivskyi July 11, 2019 20:21
@@ -808,96 +808,347 @@ class CalledProcessError(Exception):
output: Optional[_TXT] = ...,
stderr: Optional[_TXT] = ...) -> None: ...

class Popen:
class _Popen(Generic[AnyStr]):
Copy link
Member

Choose a reason for hiding this comment

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

Actually now I am not sure underscore+alias is actually needed (and was probably not needed for CompletedProcess) since these types will appear in error messages and may confuse people. But let's see what other people think @JelleZijlstra @srittau

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm going to drop it

pid = 0
returncode = 0

# Technically it is wrong that Popen provides __new__ instead of __init__
# but this shouldn't come up hopefully?
Copy link
Member

Choose a reason for hiding this comment

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

Sure, the only way to know how important it is to be precise is to try it, so I propose to ahead.

Copy link

Choose a reason for hiding this comment

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

Yuck, so if anyone overrides __init__ (like some of our code does to provide additional features), then the returned type is wrong.

What's the reason __new__ needs to be used over __init__?

Copy link
Collaborator

Choose a reason for hiding this comment

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

I'm guessing the reason for using __new__ is that with __init__, there wouldn't be a way to specify Popen's contained type because it doesn't come directly from any of the parameter types.

For what it's worth, the way pytype gets around this kind of problem is with a pyi extension we refer to as "mutations", e.g.:

class Popen:
  @overload
  def __init__(self, ...):
    self = Popen[str]
  @overload
  def __init__(self, ...):
    self = Popen[bytes]

On a related note, I just finished rolling out this change at Google, and while the __new__/__init__ distinction caused a few false positives, the more precise Popen types flagged tons of places where subprocess output was being used in Python 3-incompatible ways, which was incredibly useful.

@msullivan msullivan merged commit 3ad3ed8 into master Jul 16, 2019
@msullivan msullivan deleted the more-subprocess branch July 16, 2019 22:41
sfermigier pushed a commit to sfermigier/typeshed that referenced this pull request Jul 18, 2019
This takes advantage of a recent mypy change to respect the return
type of `__new__`. Using that it does the same tedious overloads
as `run` and `check_output`.
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.

4 participants