Skip to content

Commit

Permalink
Merge pull request #543 from google/google_sync
Browse files Browse the repository at this point in the history
Google sync
  • Loading branch information
rchen152 committed Apr 2, 2020
2 parents a7ae6ab + 430779a commit 51bcd3a
Show file tree
Hide file tree
Showing 137 changed files with 3,087 additions and 3,467 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG
@@ -1,3 +1,6 @@
Version 2020.04.01
* Do some test restructuring and cleanup.

Version 2020.03.19
* Fix a couple of pytype crashes.
* Do not allow mixing string types in IO.write() in Python 3.
Expand Down
9 changes: 8 additions & 1 deletion README.md
Expand Up @@ -93,11 +93,18 @@ interpreter in `$PATH` for the Python version of the code you're analyzing.

Platform support:

* Pytype is currently developed and tested on Linux, which is the main supported
* Pytype is currently developed and tested on Linux\*, which is the main supported
platform.
* Installation on MacOSX requires OSX 10.7 or higher and Xcode v8 or higher.
* Windows is currently not supported unless you use [WSL][wsl].

<sub>\*
Note: On Alpine Linux, installing may fail due to issues with upstream
dependencies. See the details of
<a href="https://github.com/scikit-build/ninja-python-distributions/issues/27">
this issue</a> for a possible fix.
</sub>

## Installing

Pytype can be installed via pip. Note that the installation requires `wheel`
Expand Down
2 changes: 1 addition & 1 deletion docs/faq.md
Expand Up @@ -127,7 +127,7 @@ class LoggerMixin(LoggerMixinInterface):
def log(self, msg: str):
self._log.print(f"{self.name()}: {msg}")

class Person(Logger):
class Person(LoggerMixinInterface):
... # Other initialization
def name(self):
return self._name
Expand Down
9 changes: 8 additions & 1 deletion docs/index.md
Expand Up @@ -91,11 +91,18 @@ interpreter in `$PATH` for the Python version of the code you're analyzing.

Platform support:

* Pytype is currently developed and tested on Linux, which is the main supported
* Pytype is currently developed and tested on Linux\*, which is the main supported
platform.
* Installation on MacOSX requires OSX 10.7 or higher and Xcode v8 or higher.
* Windows is currently not supported unless you use [WSL][wsl].

<sub>\*
Note: On Alpine Linux, installing may fail due to issues with upstream
dependencies. See the details of
<a href="https://github.com/scikit-build/ninja-python-distributions/issues/27">
this issue</a> for a possible fix.
</sub>

## Installing

Pytype can be installed via pip. Note that the installation requires `wheel`
Expand Down
2 changes: 1 addition & 1 deletion pytype/__version__.py
@@ -1,2 +1,2 @@
# pylint: skip-file
__version__ = '2020.03.19'
__version__ = '2020.04.01'
5 changes: 3 additions & 2 deletions pytype/abstract.py
Expand Up @@ -2491,7 +2491,7 @@ def update_sig(method):
if nitem in self.template:
raise abstract_utils.GenericTypeError(
self, ("Generic class [%s] and its nested generic class [%s] "
"cannot use same type variable %s.")
"cannot use the same type variable %s.")
% (self.full_name, cls.full_name, item.name))

self._load_all_formal_type_parameters() # Throw exception if there is error
Expand Down Expand Up @@ -3010,7 +3010,8 @@ def _inner_cls_check(self, last_frame):
inner_cls_types = value.collect_inner_cls_types()
inner_cls_types.update([(value, item.with_module(None))
for item in value.template])
for cls, item in inner_cls_types:
# Report errors in a deterministic order.
for cls, item in sorted(inner_cls_types, key=lambda typ: typ[1].name):
if item in all_type_parameters:
self.vm.errorlog.invalid_annotation(
self.vm.simple_stack(self.get_first_opcode()), item,
Expand Down
7 changes: 3 additions & 4 deletions pytype/abstract_test.py
Expand Up @@ -119,7 +119,7 @@ def test_call_wrong_argcount(self):
self.assertEqual(self._node, node)
self.assertIsInstance(abstract_utils.get_atomic_value(result),
abstract.Unsolvable)
self.assertRegexpMatches(str(self._vm.errorlog), "missing-parameter")
six.assertRegex(self, str(self._vm.errorlog), "missing-parameter")

def test_call_wrong_keywords(self):
self._vm.push_frame(frame_state.SimpleFrame())
Expand All @@ -130,9 +130,8 @@ def test_call_wrong_keywords(self):
self.assertEqual(self._node, node)
self.assertIsInstance(abstract_utils.get_atomic_value(result),
abstract.Unsolvable)
self.assertRegexpMatches(
str(self._vm.errorlog),
r"foo.*isinstance.*\[wrong-keyword-args\]")
six.assertRegex(self, str(self._vm.errorlog),
r"foo.*isinstance.*\[wrong-keyword-args\]")

def test_is_instance(self):
def check(expected, left, right):
Expand Down
8 changes: 7 additions & 1 deletion pytype/annotations_util.py
@@ -1,5 +1,7 @@
"""Utilities for inline type annotations."""

import sys

from pytype import abstract
from pytype import abstract_utils
from pytype import mixin
Expand Down Expand Up @@ -157,7 +159,11 @@ def convert_annotations_list(self, node, annotations_list):
def convert_class_annotations(self, node, raw_annotations):
"""Convert a name -> raw_annot dict to annotations."""
annotations = {}
for name, t in raw_annotations.items():
raw_items = raw_annotations.items()
if sys.version_info[:2] < (3, 6):
# Make sure annotation errors are reported in a deterministic order.
raw_items = sorted(raw_items, key=str)
for name, t in raw_items:
# Don't use the parameter name, since it's often something unhelpful
# like `0`.
annot = self._process_one_annotation(
Expand Down
16 changes: 8 additions & 8 deletions pytype/debug_test.py
Expand Up @@ -43,7 +43,7 @@ def testAsciiTree(self):
n7 = Node("n7", n5)
del n4, n6 # make pylint happy
s = debug.ascii_tree(n1, lambda n: n.outgoing)
self.assertMultiLineEqual(textwrap.dedent("""\
self.assertMultiLineEqual(textwrap.dedent("""
Node(n1)
|
+-Node(n2)
Expand All @@ -57,47 +57,47 @@ def testAsciiTree(self):
+-Node(n6)
|
+-Node(n7)
"""), s)
""").lstrip(), s)
s = debug.ascii_tree(n7, lambda n: n.incoming)
self.assertMultiLineEqual(textwrap.dedent("""\
self.assertMultiLineEqual(textwrap.dedent("""
Node(n7)
|
+-Node(n5)
|
+-Node(n1)
"""), s)
""").lstrip(), s)

def testAsciiGraph(self):
n1 = Node("n1")
n2 = Node("n2", n1)
n3 = Node("n3", n2)
n3.connect_to(n1)
s = debug.ascii_tree(n1, lambda n: n.outgoing)
self.assertMultiLineEqual(textwrap.dedent("""\
self.assertMultiLineEqual(textwrap.dedent("""
Node(n1)
|
+-Node(n2)
|
+-Node(n3)
|
+-[Node(n1)]
"""), s)
""").lstrip(), s)

def testAsciiGraphWithCustomText(self):
n1 = Node("n1")
n2 = Node("n2", n1)
n3 = Node("n3", n2)
n3.connect_to(n1)
s = debug.ascii_tree(n1, lambda n: n.outgoing, lambda n: n.name.upper())
self.assertMultiLineEqual(textwrap.dedent("""\
self.assertMultiLineEqual(textwrap.dedent("""
N1
|
+-N2
|
+-N3
|
+-[N1]
"""), s)
""").lstrip(), s)

def testRootCause(self):
n1 = self.prog.NewCFGNode()
Expand Down
14 changes: 8 additions & 6 deletions pytype/directors_test.py
Expand Up @@ -3,6 +3,7 @@

from pytype import directors
from pytype import errors
import six
import unittest

_TEST_FILENAME = "my_file.py"
Expand Down Expand Up @@ -99,6 +100,7 @@ class DirectorTest(unittest.TestCase):

@classmethod
def setUpClass(cls):
super(DirectorTest, cls).setUpClass()
# Invoking the _error_name decorator will register the name as a valid
# error name.
for name in ["test-error", "test-other-error"]:
Expand Down Expand Up @@ -296,7 +298,7 @@ def check_warning(message_regex, text):
error = list(self._errorlog)[0]
self.assertEqual(_TEST_FILENAME, error._filename)
self.assertEqual(1, error.lineno)
self.assertRegexpMatches(str(error), message_regex)
six.assertRegex(self, str(error), message_regex)

check_warning("Unknown pytype directive.*disalbe.*",
"# pytype: disalbe=test-error")
Expand Down Expand Up @@ -349,7 +351,7 @@ def test_strings_that_look_like_directives(self):
}, self._director.type_comments)

def test_type_comment_on_multiline_value(self):
self._create("""\
self._create("""
v = [
("hello",
"world", # type: should_be_ignored
Expand All @@ -358,11 +360,11 @@ def test_type_comment_on_multiline_value(self):
] # type: dict
""")
self.assertEqual({
3: ("]", "dict"),
4: ("]", "dict"),
}, self._director.type_comments)

def test_type_comment_with_trailing_comma(self):
self._create("""\
self._create("""
v = [
("hello",
"world"
Expand All @@ -375,8 +377,8 @@ def test_type_comment_with_trailing_comma(self):
] # type: dict
""")
self.assertEqual({
3: ("]", "dict"),
9: ("]", "dict"),
4: ("]", "dict"),
10: ("]", "dict"),
}, self._director.type_comments)


Expand Down
36 changes: 18 additions & 18 deletions pytype/errors_test.py
Expand Up @@ -73,21 +73,21 @@ def test_no_traceback_no_opcode(self):
def test_traceback(self):
stack = test_utils.fake_stack(errors.MAX_TRACEBACK_LENGTH + 1)
error = errors.Error.with_stack(stack, errors.SEVERITY_ERROR, "")
self.assertMultiLineEqual(error._traceback, textwrap.dedent("""\
self.assertMultiLineEqual(error._traceback, textwrap.dedent("""
Called from (traceback):
line 0, in function0
line 1, in function1
line 2, in function2"""))
line 2, in function2""").lstrip())

@errors._error_name(_TEST_ERROR)
def test_truncated_traceback(self):
stack = test_utils.fake_stack(errors.MAX_TRACEBACK_LENGTH + 2)
error = errors.Error.with_stack(stack, errors.SEVERITY_ERROR, "")
self.assertMultiLineEqual(error._traceback, textwrap.dedent("""\
self.assertMultiLineEqual(error._traceback, textwrap.dedent("""
Called from (traceback):
line 0, in function0
...
line 3, in function3"""))
line 3, in function3""").lstrip())

def test__error_name(self):
# This should be true as long as at least one method is annotated with
Expand Down Expand Up @@ -119,7 +119,7 @@ def test_write_to_csv(self):
errorlog.print_to_csv_file(filename)
with open(filename, "r") as fi:
rows = list(csv.reader(fi, delimiter=","))
self.assertEqual(2, len(rows))
self.assertEqual(len(rows), 2)
for i, row in enumerate(rows):
filename, lineno, name, actual_message, actual_details = row
self.assertEqual(filename, "foo.py")
Expand All @@ -138,12 +138,12 @@ def test_write_to_csv_with_traceback(self):
errorlog.print_to_csv_file(filename)
with open(filename, "r") as fi:
(_, _, _, _, actual_details), = list(csv.reader(fi, delimiter=","))
self.assertMultiLineEqual(actual_details, textwrap.dedent("""\
self.assertMultiLineEqual(actual_details, textwrap.dedent("""
some
details
Called from (traceback):
line 0, in function0"""))
line 0, in function0""").lstrip())


class ErrorLogBaseTest(unittest.TestCase):
Expand All @@ -153,7 +153,7 @@ def test_error(self):
errorlog = errors.ErrorLog()
op = test_utils.FakeOpcode("foo.py", 123, "foo")
errorlog.error(op.to_stack(), "unknown attribute %s" % "xyz")
self.assertEqual(1, len(errorlog))
self.assertEqual(len(errorlog), 1)
e = list(errorlog)[0] # iterate the log and save the first error.
self.assertEqual(errors.SEVERITY_ERROR, e._severity)
self.assertEqual("unknown attribute xyz", e._message)
Expand All @@ -164,18 +164,18 @@ def test_error(self):
def test_error_with_details(self):
errorlog = errors.ErrorLog()
errorlog.error(None, "My message", "one\ntwo")
self.assertEqual(textwrap.dedent("""\
self.assertEqual(textwrap.dedent("""
My message [test-error]
one
two
"""), str(errorlog))
""").lstrip(), str(errorlog))

@errors._error_name(_TEST_ERROR)
def test_warn(self):
errorlog = errors.ErrorLog()
op = test_utils.FakeOpcode("foo.py", 123, "foo")
errorlog.warn(op.to_stack(), "unknown attribute %s", "xyz")
self.assertEqual(1, len(errorlog))
self.assertEqual(len(errorlog), 1)
e = list(errorlog)[0] # iterate the log and save the first error.
self.assertEqual(errors.SEVERITY_WARNING, e._severity)
self.assertEqual("unknown attribute xyz", e._message)
Expand All @@ -188,11 +188,11 @@ def test_has_error(self):
self.assertFalse(errorlog.has_error())
# A warning is part of the error log, but isn't severe.
errorlog.warn(None, "A warning")
self.assertEqual(1, len(errorlog))
self.assertEqual(len(errorlog), 1)
self.assertFalse(errorlog.has_error())
# An error is severe.
errorlog.error(None, "An error")
self.assertEqual(2, len(errorlog))
self.assertEqual(len(errorlog), 2)
self.assertTrue(errorlog.has_error())

@errors._error_name(_TEST_ERROR)
Expand All @@ -203,7 +203,7 @@ def test_duplicate_error_no_traceback(self):
errorlog.error(stack[-1:], "error") # no traceback
# Keep the error with no traceback.
unique_errors = errorlog.unique_sorted_errors()
self.assertEqual(1, len(unique_errors))
self.assertEqual(len(unique_errors), 1)
self.assertIsNone(unique_errors[0]._traceback)

@errors._error_name(_TEST_ERROR)
Expand All @@ -230,10 +230,10 @@ def test_duplicate_error_shorter_traceback(self):
errorlog.error(stack[-2:], "error") # shorter traceback
# Keep the error with a shorter traceback.
unique_errors = errorlog.unique_sorted_errors()
self.assertEqual(1, len(unique_errors))
self.assertMultiLineEqual(unique_errors[0]._traceback, textwrap.dedent("""\
self.assertEqual(len(unique_errors), 1)
self.assertMultiLineEqual(unique_errors[0]._traceback, textwrap.dedent("""
Called from (traceback):
line 1, in function1"""))
line 1, in function1""").lstrip())

@errors._error_name(_TEST_ERROR)
def test_unique_errors(self):
Expand All @@ -248,7 +248,7 @@ def test_unique_errors(self):
errorlog.error([backframe2, current_frame], "error")
# Keep both errors, since the tracebacks are different.
unique_errors = errorlog.unique_sorted_errors()
self.assertEqual(2, len(unique_errors))
self.assertEqual(len(unique_errors), 2)
self.assertSetEqual(set(errorlog), set(unique_errors))


Expand Down
6 changes: 3 additions & 3 deletions pytype/load_pytd_test.py
Expand Up @@ -415,10 +415,10 @@ def testStarImport(self):
loaded_ast = self._LoadPickledModule(d, bar)
loaded_ast.Visit(visitors.VerifyLookup())
self.assertMultiLineEqual(pytd_utils.Print(loaded_ast),
textwrap.dedent("""\
textwrap.dedent("""
import foo
bar.A = foo.A"""))
bar.A = foo.A""").lstrip())

def testFunctionAlias(self):
with file_utils.Tempdir() as d:
Expand Down Expand Up @@ -482,7 +482,7 @@ def testPython3Builtins(self):
# Test that we read python3 builtins from builtin.pytd if we pass a (3, 6)
# version to the loader.
with file_utils.Tempdir() as d:
d.create_file("a.pyi", """\
d.create_file("a.pyi", """
from typing import AsyncGenerator
class A(AsyncGenerator[str]): ...""")
loader = load_pytd.Loader("base",
Expand Down

0 comments on commit 51bcd3a

Please sign in to comment.