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 fast parser handles type annotations + type comments better #2735

Merged
merged 2 commits into from
Jan 23, 2017
Merged

Make fast parser handles type annotations + type comments better #2735

merged 2 commits into from
Jan 23, 2017

Conversation

afrieder
Copy link
Contributor

@afrieder afrieder commented Jan 21, 2017

This PR has two parts:

  1. Disallows two return annotations (ie addresses Fast parser does not error on functions with both type comments and type annotations #2733)
  2. Enforces that type annotations and the type comment are consistent.

I assume 1 is not controversial, but 2 might be.

The point of it is to prevent:

def f(x: str):
  # type: (int) -> int
  return x

which is currently perfectly allowed.

Thus, this code checks, for every argument with a type annotation, that the type in the type comment is identical.
The way the code works, # type: (...) -> int will always pass.
The reason this might controversial is that it does string comparison, so it's not always right. In fact, it's very simple to get a false positive:

def f(x: Union[int, str]):
  # type: (Union[str, int]) -> int
  return 5

will now complain about an inconsistent type signature.

Now, I feel this is fine, since if you're going to go through the trouble of using both a type annotation and a non ... type comment, then you might as well make them the same. The only reason this might be an actual problem is for something like:

T = Union[int, str]
def f(x: Union[int, str]):
  # type: (T) -> int
  return 5

Which fails. Because we only keep a single source of truth for what the arg types are, we cannot postpone this check until we have actual types without a large and unnecessary (imo) refactor.

So in summary, I think it's perfectly fine to insist that if users are going to use both type annotations and type comments in a function that they match identically.
But if the Powers That Be disagree, I'll happily remove the second part from this PR.

@afrieder
Copy link
Contributor Author

afrieder commented Jan 21, 2017

I should also add that because of how the typed AST handles type comments,

def f(x: int):
  return x

and

def f(x  # type: int
   ):
  return x

are indistinguishable.

This is... annoying, because in the old parser

def f(x  # type: int
  ):
  # type: (...) -> int
  return x

is allowed, while

def f(x: int):
  # type: (...) -> 5
  return x

is not.

Since we cannot replicate this behavior, I chose to just enforce that the two methods of annotating convey the same type information.

@afrieder afrieder changed the title Fast parser handles type annotations + type comments better Make fast parser handles type annotations + type comments better Jan 22, 2017
@ddfisher ddfisher self-requested a review January 23, 2017 19:20
@ddfisher
Copy link
Collaborator

Thanks for the PR! To my understanding, there's no reason anyone should be mixing type comments and type annotations, so it would be better to disallow mixing them entirely rather than to check for consistency. I agree that it would be better if typed_ast distinguished between per-argument type comments and type annotations, but I don't think that's hugely important. Because of that limitation, let's just disallow type annotations for all non-ellipsis-parameter type comments.

Copy link
Collaborator

@ddfisher ddfisher left a comment

Choose a reason for hiding this comment

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

(see comment above)

@gvanrossum
Copy link
Member

gvanrossum commented Jan 23, 2017 via email

Copy link
Collaborator

@ddfisher ddfisher left a comment

Choose a reason for hiding this comment

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

Thanks for the update! Just one tiny nit.

arg_types = [a.type_annotation if a.type_annotation is not None else AnyType()
for a in args]
else:
# PEP 484 disallows both type annotations and type comments
if n.returns or any(a.type_annotation for a in args):
Copy link
Collaborator

Choose a reason for hiding this comment

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

It's slightly better to explicitly check that a.type_annotation is not None than just to check the truthiness here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed (in both places).
Completely unrelated, sorry about clogging AppVeyor.

@@ -284,6 +285,9 @@ def visit_FunctionDef(self, n: ast27.FunctionDef) -> Statement:
arg_types = [a.type_annotation if a.type_annotation is not None else AnyType()
for a in args]
else:
# PEP 484 disallows both type annotations and type comments
if any(a.type_annotation for a in args):
Copy link
Collaborator

Choose a reason for hiding this comment

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

(and here also)

@afrieder
Copy link
Contributor Author

Cleaned up the commits, should be good to go? It's all much cleaner now.

@ddfisher
Copy link
Collaborator

Looks good, thanks! I'll merge once the tests finish running.

FYI we do squash merges on PRs, so (aside from having things be understandable during review) you don't need to worry about a nice commit history.

@ddfisher ddfisher merged commit 1c84af3 into python:master Jan 23, 2017
@afrieder afrieder deleted the fail branch January 23, 2017 22:48
@afrieder
Copy link
Contributor Author

Ack.
It was purely for review legibility.

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