[ 
https://issues.apache.org/jira/browse/BEAM-3143?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=16268009#comment-16268009
 ] 

ASF GitHub Bot commented on BEAM-3143:
--------------------------------------

luke-zhu closed pull request #4176: [BEAM-3143] Type Inference Compatibility 
with Python 3
URL: https://github.com/apache/beam/pull/4176
 
 
   

This is a PR merged from a forked repository.
As GitHub hides the original diff on merge, it is displayed below for
the sake of provenance:

As this is a foreign pull request (from a fork), the diff is supplied
below (as it won't show otherwise due to GitHub magic):

diff --git a/sdks/python/apache_beam/typehints/__init__.py 
b/sdks/python/apache_beam/typehints/__init__.py
index e89afa1285a..8d89efb21c6 100644
--- a/sdks/python/apache_beam/typehints/__init__.py
+++ b/sdks/python/apache_beam/typehints/__init__.py
@@ -18,5 +18,6 @@
 """A package defining the syntax and decorator semantics for type-hints."""
 
 # pylint: disable=wildcard-import
+from __future__ import absolute_import
 from apache_beam.typehints.typehints import *
 from apache_beam.typehints.decorators import *
diff --git a/sdks/python/apache_beam/typehints/decorators.py 
b/sdks/python/apache_beam/typehints/decorators.py
index 89dc6afa34c..c6792747b99 100644
--- a/sdks/python/apache_beam/typehints/decorators.py
+++ b/sdks/python/apache_beam/typehints/decorators.py
@@ -83,6 +83,7 @@ def foo((a, b)):
 defined, or before importing a module containing type-hinted functions.
 """
 
+from __future__ import absolute_import
 import inspect
 import types
 
@@ -92,6 +93,7 @@ def foo((a, b)):
 from apache_beam.typehints.typehints import SimpleTypeHintError
 from apache_beam.typehints.typehints import check_constraint
 from apache_beam.typehints.typehints import validate_composite_type_param
+from six.moves import zip
 
 __all__ = [
     'with_input_types',
@@ -567,11 +569,13 @@ def __getattr__(self, attr):
       return self.__iter__()
     return getattr(self.internal_gen, attr)
 
-  def next(self):
+  def __next__(self):
     next_val = next(self.internal_gen)
     self.interleave_func(next_val)
     return next_val
 
+  next = __next__
+
   def __iter__(self):
     while True:
       x = next(self.internal_gen)
diff --git a/sdks/python/apache_beam/typehints/disassembly.py 
b/sdks/python/apache_beam/typehints/disassembly.py
new file mode 100644
index 00000000000..506459b98ca
--- /dev/null
+++ b/sdks/python/apache_beam/typehints/disassembly.py
@@ -0,0 +1,230 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+"""The Python 3 disassembler source code backported to be compatible with
+with Python 2.7.
+"""
+
+from __future__ import absolute_import
+from __future__ import print_function
+
+import collections
+import dis
+import sys
+
+
+def get_instructions(x, first_line=None):
+  """Iterator for the opcodes in methods, functions or code
+  Generates a series of Instruction named tuples giving the details of
+  each operations in the supplied code.
+  If *first_line* is not None, it indicates the line number that should
+  be reported for the first source line in the disassembled code.
+  Otherwise, the source line information (if any) is taken directly from
+  the disassembled code object.
+  """
+  co = _get_code_object(x)
+  cell_names = co.co_cellvars + co.co_freevars
+  linestarts = dict(dis.findlinestarts(co))
+  if first_line is not None:
+    line_offset = first_line - co.co_firstlineno
+  else:
+    line_offset = 0
+  return _get_instructions_bytes(co.co_code, co.co_varnames, co.co_names,
+                                 co.co_consts, cell_names, linestarts,
+                                 line_offset)
+
+
+def _get_code_object(x):
+  """Helper to handle methods, functions, generators, strings
+  and raw code objects"""
+  if hasattr(x, '__func__'):  # Method
+    x = x.__func__
+  if hasattr(x, '__code__'):  # Function
+    x = x.__code__
+  if hasattr(x, 'gi_code'):  # Generator
+    x = x.gi_code
+  if isinstance(x, str):  # Source code
+    x = _try_compile(x, "<disassembly>")
+  if hasattr(x, 'co_code'):  # Code object
+    return x
+  raise TypeError("don't know how to disassemble %s objects" %
+                  type(x).__name__)
+
+
+def _try_compile(source, name):
+  """Attempts to compile the given source, first as an expression and
+     then as a statement if the first approach fails.
+     Utility function to accept strings in functions that otherwise
+     expect code objects
+  """
+  try:
+    c = compile(source, name, 'eval')
+  except SyntaxError:
+    c = compile(source, name, 'exec')
+  return c
+
+
+def _get_instructions_bytes(code, varnames=None, names=None, constants=None,
+                            cells=None, linestarts=None, line_offset=0):
+  """Iterate over the instructions in a bytecode string.
+  Generates a sequence of Instruction namedtuples giving the details of each
+  opcode.  Additional information about the code's runtime environment
+  (e.g. variable names, constants) can be specified using optional
+  arguments.
+  """
+  labels = dis.findlabels(code)
+  starts_line = None
+  for offset, op, arg in _unpack_opargs(code):
+    if linestarts is not None:
+      starts_line = linestarts.get(offset, None)
+      if starts_line is not None:
+        starts_line += line_offset
+    is_jump_target = offset in labels
+    argval = None
+    argrepr = ''
+    if arg is not None:
+      # Set argval to the dereferenced value of the argument when
+      # available, and argrepr to the string representation of argval.
+      # _disassemble_bytes needs the string repr of the
+      # raw name index for LOAD_GLOBAL, LOAD_CONST, etc.
+      argval = arg
+      if op in dis.hasconst:
+        argval, argrepr = _get_const_info(arg, constants)
+      elif op in dis.hasname:
+        argval, argrepr = _get_name_info(arg, names)
+      elif op in dis.hasjrel:
+        argval = offset + 3 + arg
+        argrepr = "to " + repr(argval)
+      elif op in dis.haslocal:
+        argval, argrepr = _get_name_info(arg, varnames)
+      elif op in dis.hascompare:
+        argval = dis.cmp_op[arg]
+        argrepr = argval
+      elif op in dis.hasfree:
+        argval, argrepr = _get_name_info(arg, cells)
+      elif sys.version_info[0] == 3 and op in dis.hasnargs:
+        argrepr = "%d positional, %d keyword pair" % (arg % 256, arg // 256)
+    yield Instruction(dis.opname[op], op,
+                      arg, argval, argrepr,
+                      offset, starts_line, is_jump_target)
+
+
+def _unpack_opargs(code):
+  # enumerate() is not an option, since we sometimes process
+  # multiple elements on a single pass through the loop
+  extended_arg = 0
+  n = len(code)
+  i = 0
+  while i < n:
+    if sys.version_info[0] == 3:
+      op = code[i]
+    else:
+      op = ord(code[i])
+    offset = i
+    i = i + 1
+    arg = None
+    if op >= dis.HAVE_ARGUMENT:
+      if sys.version_info[0] == 3:
+        arg = code[i] + code[i + 1] * 256 + extended_arg
+      else:
+        arg = ord(code[i]) + ord(code[i + 1]) * 256 + extended_arg
+      extended_arg = 0
+      i = i + 2
+      if op == dis.EXTENDED_ARG:
+        extended_arg = arg * 65536
+    yield (offset, op, arg)
+
+
+def _get_const_info(const_index, const_list):
+  """Helper to get optional details about const references
+     Returns the dereferenced constant and its repr if the constant
+     list is defined.
+     Otherwise returns the constant index and its repr().
+  """
+  argval = const_index
+  if const_list is not None:
+    argval = const_list[const_index]
+  return argval, repr(argval)
+
+
+def _get_name_info(name_index, name_list):
+  """Helper to get optional details about named references
+     Returns the dereferenced name as both value and repr if the name
+     list is defined.
+     Otherwise returns the name index and its repr().
+  """
+  argval = name_index
+  if name_list is not None:
+    argval = name_list[name_index]
+    argrepr = argval
+  else:
+    argrepr = repr(argval)
+  return argval, argrepr
+
+
+_Instruction = collections.namedtuple(
+    "_Instruction",
+    "opname opcode arg argval argrepr offset starts_line is_jump_target")
+
+
+class Instruction(_Instruction):
+  """Details for a bytecode operation
+     Defined fields:
+       opname - human readable name for operation
+       opcode - numeric code for operation
+       arg - numeric argument to operation (if any), otherwise None
+       argval - resolved arg value (if known), otherwise same as arg
+       argrepr - human readable description of operation argument
+       offset - start index of operation within bytecode sequence
+       starts_line - line started by this opcode (if any), otherwise None
+       is_jump_target - True if other code jumps to here, otherwise False
+  """
+
+  def _disassemble(self, lineno_width=3, mark_as_current=False):
+    """Format instruction details for inclusion in disassembly output
+    *lineno_width* sets the width of the line number field (0 omits it)
+    *mark_as_current* inserts a '-->' marker arrow as part of the line
+    """
+    fields = []
+    # Column: Source code line number
+    if lineno_width:
+      if self.starts_line is not None:
+        lineno_fmt = "%%%dd" % lineno_width
+        fields.append(lineno_fmt % self.starts_line)
+      else:
+        fields.append(' ' * lineno_width)
+    # Column: Current instruction indicator
+    if mark_as_current:
+      fields.append('-->')
+    else:
+      fields.append('   ')
+    # Column: Jump target marker
+    if self.is_jump_target:
+      fields.append('>>')
+    else:
+      fields.append('  ')
+    # Column: Instruction offset from start of code sequence
+    fields.append(repr(self.offset).rjust(4))
+    # Column: Opcode name
+    fields.append(self.opname.ljust(20))
+    # Column: Opcode argument
+    if self.arg is not None:
+      fields.append(repr(self.arg).rjust(5))
+      # Column: Opcode argument details
+      if self.argrepr:
+        fields.append('(' + self.argrepr + ')')
+    return ' '.join(fields).rstrip()
diff --git a/sdks/python/apache_beam/typehints/disassembly_test.py 
b/sdks/python/apache_beam/typehints/disassembly_test.py
new file mode 100644
index 00000000000..c6569c373fe
--- /dev/null
+++ b/sdks/python/apache_beam/typehints/disassembly_test.py
@@ -0,0 +1,96 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+"""Tests for apache_beam.typehints.disassembly."""
+
+from __future__ import absolute_import
+
+import unittest
+
+from apache_beam.typehints.disassembly import Instruction, get_instructions
+
+# Uncomment the line below to compare with the Python 3.4 dis library
+# from dis import Instruction, get_instructions
+
+
+class InstructionsTest(unittest.TestCase):
+  def testFunction(self):
+    actual = list(get_instructions(lambda x: [x, 10 - x]))
+    expected = [Instruction(opname='LOAD_FAST', opcode=124, arg=0, argval='x',
+                            argrepr='x', offset=0, starts_line=32,
+                            is_jump_target=False),
+                Instruction(opname='LOAD_CONST', opcode=100, arg=1, argval=10,
+                            argrepr='10', offset=3, starts_line=None,
+                            is_jump_target=False),
+                Instruction(opname='LOAD_FAST', opcode=124, arg=0, argval='x',
+                            argrepr='x', offset=6, starts_line=None,
+                            is_jump_target=False),
+                Instruction(opname='BINARY_SUBTRACT', opcode=24, arg=None,
+                            argval=None, argrepr='', offset=9, 
starts_line=None,
+                            is_jump_target=False),
+                Instruction(opname='BUILD_LIST', opcode=103, arg=2, argval=2,
+                            argrepr='', offset=10, starts_line=None,
+                            is_jump_target=False),
+                Instruction(opname='RETURN_VALUE', opcode=83, arg=None,
+                            argval=None, argrepr='', offset=13,
+                            starts_line=None, is_jump_target=False)]
+    self.assertEqual(actual, expected)
+
+  def testLambda(self):
+    def f(x):
+      z = 2
+      return z - x
+    actual = list(get_instructions(f))
+    expected = [
+        Instruction(opname='LOAD_CONST', opcode=100, arg=1, argval=2,
+                    argrepr='2', offset=0, starts_line=55,
+                    is_jump_target=False),
+        Instruction(opname='STORE_FAST', opcode=125, arg=1, argval='z',
+                    argrepr='z', offset=3, starts_line=None,
+                    is_jump_target=False),
+        Instruction(opname='LOAD_FAST', opcode=124, arg=1, argval='z',
+                    argrepr='z', offset=6, starts_line=56,
+                    is_jump_target=False),
+        Instruction(opname='LOAD_FAST', opcode=124, arg=0, argval='x',
+                    argrepr='x', offset=9, starts_line=None,
+                    is_jump_target=False),
+        Instruction(opname='BINARY_SUBTRACT', opcode=24, arg=None, argval=None,
+                    argrepr='', offset=12, starts_line=None,
+                    is_jump_target=False),
+        Instruction(opname='RETURN_VALUE', opcode=83, arg=None, argval=None,
+                    argrepr='', offset=13, starts_line=None,
+                    is_jump_target=False)]
+    self.assertEqual(actual, expected)
+
+  def testBoundMethod(self):
+    class A(object):
+      def m(self, x):
+        return x + 15.3
+    actual = list(get_instructions(A().m))
+    expected = [Instruction(opname='LOAD_FAST', opcode=124, arg=1, argval='x',
+                            argrepr='x', offset=0, starts_line=82,
+                            is_jump_target=False),
+                Instruction(opname='LOAD_CONST', opcode=100, arg=1, 
argval=15.3,
+                            argrepr='15.3', offset=3, starts_line=None,
+                            is_jump_target=False),
+                Instruction(opname='BINARY_ADD', opcode=23, arg=None,
+                            argval=None, argrepr='', offset=6, 
starts_line=None,
+                            is_jump_target=False),
+                Instruction(opname='RETURN_VALUE', opcode=83, arg=None,
+                            argval=None, argrepr='', offset=7, 
starts_line=None,
+                            is_jump_target=False)]
+    self.assertEqual(actual, expected)
diff --git a/sdks/python/apache_beam/typehints/native_type_compatibility.py 
b/sdks/python/apache_beam/typehints/native_type_compatibility.py
index 8a8e07ecb4b..1d0e76659a3 100644
--- a/sdks/python/apache_beam/typehints/native_type_compatibility.py
+++ b/sdks/python/apache_beam/typehints/native_type_compatibility.py
@@ -17,9 +17,14 @@
 
 """Module to convert Python's native typing types to Beam types."""
 
+from __future__ import absolute_import
+
 import collections
 import typing
 
+import six
+from six.moves import range
+
 from apache_beam.typehints import typehints
 
 # Describes an entry in the type map in convert_to_beam_type.
@@ -138,7 +143,7 @@ def convert_to_beam_type(typ):
     if _len_arg(typ) != arity:
       raise ValueError('expecting type %s to have arity %d, had arity %d '
                        'instead' % (str(typ), arity, _len_arg(typ)))
-  typs = [convert_to_beam_type(_get_arg(typ, i)) for i in xrange(arity)]
+  typs = [convert_to_beam_type(_get_arg(typ, i)) for i in range(arity)]
   if arity == 0:
     # Nullary types (e.g. Any) don't accept empty tuples as arguments.
     return matched_entry.beam_type
@@ -161,6 +166,6 @@ def convert_to_beam_types(args):
     a dictionary with the same keys, and values which have been converted.
   """
   if isinstance(args, dict):
-    return {k: convert_to_beam_type(v) for k, v in args.iteritems()}
+    return {k: convert_to_beam_type(v) for k, v in six.iteritems(args)}
   else:
     return [convert_to_beam_type(v) for v in args]
diff --git 
a/sdks/python/apache_beam/typehints/native_type_compatibility_test.py 
b/sdks/python/apache_beam/typehints/native_type_compatibility_test.py
index 4171507f345..c05108b66c2 100644
--- a/sdks/python/apache_beam/typehints/native_type_compatibility_test.py
+++ b/sdks/python/apache_beam/typehints/native_type_compatibility_test.py
@@ -17,9 +17,12 @@
 
 """Test for Beam type compatibility library."""
 
-import typing
+from __future__ import absolute_import
+
 import unittest
 
+import typing
+
 from apache_beam.typehints import native_type_compatibility
 from apache_beam.typehints import typehints
 
diff --git a/sdks/python/apache_beam/typehints/opcodes.py 
b/sdks/python/apache_beam/typehints/opcodes.py
index dcca6d02644..55ec8ca7c21 100644
--- a/sdks/python/apache_beam/typehints/opcodes.py
+++ b/sdks/python/apache_beam/typehints/opcodes.py
@@ -31,6 +31,8 @@
 import types
 from functools import reduce
 
+import six
+
 from . import typehints
 from .trivial_inference import BoundMethod
 from .trivial_inference import Const
@@ -147,7 +149,7 @@ def binary_true_divide(state, unused_arg):
 
 def binary_subscr(state, unused_arg):
   tos = state.stack.pop()
-  if tos in (str, unicode):
+  if tos in (str, six.text_type):
     out = tos
   else:
     out = element_type(tos)
@@ -266,7 +268,9 @@ def load_attr(state, arg):
   if isinstance(o, Const) and hasattr(o.value, name):
     state.stack.append(Const(getattr(o.value, name)))
   elif (isinstance(o, type)
-        and isinstance(getattr(o, name, None), types.MethodType)):
+        and isinstance(getattr(o, name, None),
+                       (types.MethodType, types.FunctionType))):
+    # TODO(luke-zhu): Modify BoundMethod as Python 3 has no unbound method type
     state.stack.append(Const(BoundMethod(getattr(o, name))))
   else:
     state.stack.append(Any)
@@ -327,8 +331,25 @@ def call_function(state, arg, has_var=False, has_kw=False):
 
 
 def make_function(state, arg):
-  state.stack[-arg - 1:] = [Any]  # a callable
+  """Creates a function wrapped in a const from the associated code.
 
+  This currently does not handle default arguments
+  """
+  if arg == 0:
+    globals = state.f.__globals__
+    tos = state.stack.pop()
+    if isinstance(tos.value, str):
+      func_name = tos
+      code = state.stack.pop()
+      new_function = types.FunctionType(code.value,
+                                        globals,
+                                        name=func_name.value)
+    else:
+      code = tos
+      new_function = types.FunctionType(code.value, globals)
+    state.stack.append(Const(new_function))
+  else:
+    state.stack[-arg - 1:] = [Any]
 
 def make_closure(state, arg):
   state.stack[-arg - 2:] = [Any]  # a callable
diff --git a/sdks/python/apache_beam/typehints/trivial_inference.py 
b/sdks/python/apache_beam/typehints/trivial_inference.py
index a68bd18b1c3..57d2cb9ea39 100644
--- a/sdks/python/apache_beam/typehints/trivial_inference.py
+++ b/sdks/python/apache_beam/typehints/trivial_inference.py
@@ -21,8 +21,8 @@
 """
 from __future__ import absolute_import
 from __future__ import print_function
+from __future__ import division
 
-import __builtin__
 import collections
 import dis
 import pprint
@@ -30,8 +30,12 @@
 import types
 from functools import reduce
 
+from past.utils import old_div
+from six.moves import builtins, zip
+
 from apache_beam.typehints import Any
 from apache_beam.typehints import typehints
+from apache_beam.typehints.disassembly import get_instructions
 
 
 class TypeInferenceError(ValueError):
@@ -46,11 +50,14 @@ def instance_to_type(o):
   if o is None:
     return type(None)
   elif t not in typehints.DISALLOWED_PRIMITIVE_TYPES:
-    if t == types.InstanceType:
-      return o.__class__
-    elif t == BoundMethod:
+    # Todo(luke-zhu): Both of these conditions shouldn't
+    # function properly in Python 3
+    if t == BoundMethod:
       return types.MethodType
-    return t
+    try:
+      return o.__class__
+    except AttributeError:
+      return t
   elif t == tuple:
     return typehints.Tuple[[instance_to_type(item) for item in o]]
   elif t == list:
@@ -107,23 +114,25 @@ class FrameState(object):
 
   def __init__(self, f, local_vars=None, stack=()):
     self.f = f
-    if sys.version_info[0] >= 3:
-      self.co = f.__code__
-    else:
-      self.co = f.func_code
+    self.co = f.__code__
     self.vars = list(local_vars)
     self.stack = list(stack)
 
   def __eq__(self, other):
-    return self.__dict__ == other.__dict__
+    try:
+      return self.__dict__ == other.__dict__
+    except AttributeError:
+      return False
 
   def copy(self):
-    return FrameState(self.f, self.vars, self.stack)
+    return FrameState(self.f, local_vars=self.vars, stack=self.stack)
 
   def const_type(self, i):
+    """Gets the i-th constant of the unbound f as a Const."""
     return Const(self.co.co_consts[i])
 
   def closure_type(self, i):
+    """Gets the i-th closure variable of the unbound f as a Const."""
     ncellvars = len(self.co.co_cellvars)
     if i < ncellvars:
       return Any
@@ -133,8 +142,8 @@ def get_global(self, i):
     name = self.get_name(i)
     if name in self.f.__globals__:
       return Const(self.f.__globals__[name])
-    if name in __builtin__.__dict__:
-      return Const(__builtin__.__dict__[name])
+    if name in builtins.__dict__:
+      return Const(builtins.__dict__[name])
     return Any
 
   def get_name(self, i):
@@ -144,12 +153,16 @@ def __repr__(self):
     return 'Stack: %s Vars: %s' % (self.stack, self.vars)
 
   def __or__(self, other):
+    """Merges two states together to get a new state with the union
+    of the types
+    """
     if self is None:
       return other.copy()
     elif other is None:
       return self.copy()
-    return FrameState(self.f, union_list(self.vars, other.vars), union_list(
-        self.stack, other.stack))
+    return FrameState(self.f,
+                      local_vars=union_list(self.vars, other.vars),
+                      stack=union_list(self.stack, other.stack))
 
   def __ror__(self, left):
     return self | left
@@ -231,17 +244,23 @@ def infer_return_type(c, input_types, debug=False, 
depth=5):
   try:
     if hashable(c) and c in known_return_types:
       return known_return_types[c]
-    elif isinstance(c, types.FunctionType):
+    if isinstance(c, types.FunctionType):
       return infer_return_type_func(c, input_types, debug, depth)
-    elif isinstance(c, types.MethodType):
+    if isinstance(c, types.MethodType):
       if c.__self__ is not None:
         input_types = [Const(c.__self__)] + input_types
       return infer_return_type_func(c.__func__, input_types, debug, depth)
-    elif isinstance(c, BoundMethod):
-      input_types = [c.unbound.__self__.__class__] + input_types
-      return infer_return_type_func(
-          c.unbound.__func__, input_types, debug, depth)
-    elif isinstance(c, type):
+    if isinstance(c, BoundMethod):
+      try:
+        # TODO(luke-zhu): Python 3 has no unbound method type
+        # The instance type could be stored within BoundMethod
+        input_types = [c.unbound.__self__.__class__] + input_types
+        return infer_return_type_func(
+            c.unbound.__func__, input_types, debug, depth)
+      except AttributeError:
+        input_types = [Any] + input_types
+        return infer_return_type_func(c.unbound, input_types, debug, depth)
+    if isinstance(c, type):
       if c in typehints.DISALLOWED_PRIMITIVE_TYPES:
         return {
             list: typehints.List[Any],
@@ -261,7 +280,6 @@ def infer_return_type(c, input_types, debug=False, depth=5):
     else:
       return Any
 
-
 def infer_return_type_func(f, input_types, debug=False, depth=0):
   """Analyses a function to deduce its return type.
 
@@ -285,37 +303,46 @@ def infer_return_type_func(f, input_types, debug=False, 
depth=0):
   simple_ops = dict((k.upper(), v) for k, v in opcodes.__dict__.items())
 
   co = f.__code__
-  code = co.co_code
-  end = len(code)
-  pc = 0
-  extended_arg = 0
-  free = None
 
+  # TODO(robertwb): Default args via inspect module.
+  # Initialize the state
+  input_types_list = list(input_types)
+  typehints_union = [typehints.Union[()]]
+  target_length = len(co.co_varnames) - len(input_types_list)
+  local_vars = input_types_list + typehints_union * target_length
+  state = FrameState(f, local_vars=local_vars)
+
+  # Initialize the empty collections
   yields = set()
   returns = set()
-  # TODO(robertwb): Default args via inspect module.
-  local_vars = list(input_types) + [typehints.Union[()]] * (len(co.co_varnames)
-                                                            - len(input_types))
-  state = FrameState(f, local_vars)
   states = collections.defaultdict(lambda: None)
   jumps = collections.defaultdict(int)
 
+  # Initialize the instruction mappings
+  instruction = {}  # Maps an offset to its corresponding Instruction object
+  next_instruction = {}  # Maps an offset to the next offset
+  prev_instr = None
+  for instr in get_instructions(f):
+    if prev_instr:
+      next_instruction[prev_instr.offset] = instr.offset
+    instruction[instr.offset] = instr
+    prev_instr = instr
+
+  pc = 0
+  free = None
   last_pc = -1
-  while pc < end:
+  while True:
     start = pc
-    op = ord(code[pc])
-
+    instr = instruction[pc]
+    op = instr.opcode
+    arg = instr.arg
     if debug:
       print('-->' if pc == last_pc else '    ', end=' ')
       print(repr(pc).rjust(4), end=' ')
       print(dis.opname[op].ljust(20), end=' ')
     pc += 1
-    if op >= dis.HAVE_ARGUMENT:
-      arg = ord(code[pc]) + ord(code[pc + 1]) * 256 + extended_arg
-      extended_arg = 0
+    if arg is not None:
       pc += 2
-      if op == dis.EXTENDED_ARG:
-        extended_arg = arg * 65536
       if debug:
         print(str(arg).rjust(5), end=' ')
         if op in dis.hasconst:
@@ -333,7 +360,7 @@ def infer_return_type_func(f, input_types, debug=False, 
depth=0):
             free = co.co_cellvars + co.co_freevars
           print('(' + free[arg] + ')', end=' ')
 
-    # Acutally emulate the op.
+    # Actually emulate the op.
     if state is None and states[start] is None:
       # No control reaches here (yet).
       if debug:
@@ -344,27 +371,7 @@ def infer_return_type_func(f, input_types, debug=False, 
depth=0):
     opname = dis.opname[op]
     jmp = jmp_state = None
     if opname.startswith('CALL_FUNCTION'):
-      standard_args = (arg & 0xF) + (arg & 0xF0) / 8
-      var_args = 'VAR' in opname
-      kw_args = 'KW' in opname
-      pop_count = standard_args + var_args + kw_args + 1
-      if depth <= 0:
-        return_type = Any
-      elif arg & 0xF0:
-        # TODO(robertwb): Handle this case.
-        return_type = Any
-      elif isinstance(state.stack[-pop_count], Const):
-        # TODO(robertwb): Handle this better.
-        if var_args or kw_args:
-          state.stack[-1] = Any
-          state.stack[-var_args - kw_args] = Any
-        return_type = infer_return_type(state.stack[-pop_count].value,
-                                        state.stack[1 - pop_count:],
-                                        debug=debug,
-                                        depth=depth - 1)
-      else:
-        return_type = Any
-      state.stack[-pop_count:] = [return_type]
+      handle_call(opname, arg, state, debug=debug, depth=depth)
     elif (opname == 'BINARY_SUBSCR'
           and isinstance(state.stack[1], Const)
           and isinstance(state.stack[0], typehints.IndexableTypeConstraint)):
@@ -408,16 +415,27 @@ def infer_return_type_func(f, input_types, debug=False, 
depth=0):
       jmp_state = state.copy()
       jmp_state.stack.pop()
       state.stack.append(element_type(state.stack[-1]))
+    elif opname == 'BUILD_LIST':
+      jmp = pc + arg
+      jmp_state = state.copy()
+      jmp_state.stack.pop()
+      state.stack.append(element_type(state.stack[-1]))
     else:
       raise TypeInferenceError('unable to handle %s' % opname)
 
     if jmp is not None:
-      # TODO(robertwb): Is this guerenteed to converge?
+      # TODO(robertwb): Is this guaranteed to converge?
       new_state = states[jmp] | jmp_state
-      if jmp < pc and new_state != states[jmp] and jumps[pc] < 5:
-        jumps[pc] += 1
+      if jmp <= start and new_state != states[jmp] and jumps[start] < 5:
+        jumps[start] += 1
         pc = jmp
+        states[jmp] = new_state
+        continue
       states[jmp] = new_state
+    try:
+      pc = next_instruction[start]
+    except KeyError:
+      break
 
     if debug:
       print()
@@ -432,3 +450,31 @@ def infer_return_type_func(f, input_types, debug=False, 
depth=0):
   if debug:
     print(f, id(f), input_types, '->', result)
   return result
+
+def handle_call(opname, arg, state, debug=False, depth=0):
+  standard_args = (arg & 0xF) + old_div((arg & 0xF0), 8)
+  var_args = 'VAR' in opname
+  kw_args = 'KW' in opname
+  pop_count = standard_args + var_args + kw_args + 1
+  if depth <= 0:
+    return_type = Any
+  elif arg & 0xF0:
+    # TODO(robertwb): Handle this case.
+    return_type = Any
+  elif (isinstance(state.stack[-pop_count], Const) and
+        isinstance(state.stack[-pop_count].value, type) and
+        issubclass(state.stack[-pop_count].value, list)):
+    # TODO(robertwb + holden): Handle this better.
+    return_type = typehints.List[element_type(state.stack[1])]
+  elif isinstance(state.stack[-pop_count], Const):
+    # TODO(robertwb): Handle this better.
+    if var_args or kw_args:
+      state.stack[-1] = Any
+      state.stack[-var_args - kw_args] = Any
+    return_type = infer_return_type(state.stack[-pop_count].value,
+                                    state.stack[1 - pop_count:],
+                                    debug=debug,
+                                    depth=depth - 1)
+  else:
+    return_type = Any
+  state.stack[-pop_count:] = [return_type]
diff --git a/sdks/python/apache_beam/typehints/trivial_inference_test.py 
b/sdks/python/apache_beam/typehints/trivial_inference_test.py
index 37b22584723..39b013829b4 100644
--- a/sdks/python/apache_beam/typehints/trivial_inference_test.py
+++ b/sdks/python/apache_beam/typehints/trivial_inference_test.py
@@ -16,6 +16,8 @@
 #
 
 """Tests for apache_beam.typehints.trivial_inference."""
+from __future__ import absolute_import
+
 import unittest
 
 from apache_beam.typehints import trivial_inference
@@ -72,6 +74,17 @@ def func(a):
       return None
     self.assertReturnType(typehints.Union[int, type(None)], func, [int])
 
+  def testSimpleList(self):
+    self.assertReturnType(
+        typehints.List[int],
+        lambda xs: list([1, 2]),
+        [typehints.Tuple[int, ...]])
+
+    self.assertReturnType(
+        typehints.List[int],
+        lambda xs: list(xs),
+        [typehints.Tuple[int, ...]])
+
   def testListComprehension(self):
     self.assertReturnType(
         typehints.List[int],
@@ -88,6 +101,16 @@ def testTupleListComprehension(self):
         lambda xs: [x for x in xs],
         [typehints.Tuple[int, float]])
 
+  def testGeneratorComprehension(self):
+    self.assertReturnType(
+        typehints.Iterable[int],
+        lambda xs: (x for x in xs),
+        [typehints.Tuple[int, ...]])
+    self.assertReturnType(
+        typehints.Iterable[typehints.Union[int, float]],
+        lambda xs: (x for x in xs),
+        [typehints.Tuple[int, float, int]])
+
   def testGenerator(self):
 
     def foo(x, y):
@@ -157,6 +180,13 @@ def testDictComprehension(self):
         typehints.Any,
         lambda row: {f: row[f] for f in fields}, [typehints.Any])
 
+  def testNested(self):
+    def f(x):
+      def g(x):
+        return x
+      return g(x)
+    self.assertReturnType(int, f, [int])
+
 
 if __name__ == '__main__':
   unittest.main()
diff --git a/sdks/python/apache_beam/typehints/typecheck.py 
b/sdks/python/apache_beam/typehints/typecheck.py
index c47e9bacb96..ef5a537e3f6 100644
--- a/sdks/python/apache_beam/typehints/typecheck.py
+++ b/sdks/python/apache_beam/typehints/typecheck.py
@@ -20,6 +20,7 @@
 For internal use only; no backwards-compatibility guarantees.
 """
 
+from __future__ import absolute_import
 import collections
 import inspect
 import sys
@@ -35,6 +36,7 @@
 from apache_beam.typehints.typehints import CompositeTypeHintError
 from apache_beam.typehints.typehints import SimpleTypeHintError
 from apache_beam.typehints.typehints import check_constraint
+import six
 
 
 class AbstractDoFnWrapper(DoFn):
@@ -82,14 +84,14 @@ def wrapper(self, method, args, kwargs):
     except TypeCheckError as e:
       error_msg = ('Runtime type violation detected within ParDo(%s): '
                    '%s' % (self.full_label, e))
-      raise TypeCheckError, error_msg, sys.exc_info()[2]
+      six.reraise(TypeCheckError, error_msg, sys.exc_info()[2])
     else:
       return self._check_type(result)
 
   def _check_type(self, output):
     if output is None:
       return output
-    elif isinstance(output, (dict, basestring)):
+    elif isinstance(output, (dict, six.string_types)):
       object_type = type(output).__name__
       raise TypeCheckError('Returning a %s from a ParDo or FlatMap is '
                            'discouraged. Please use list("%s") if you really '
@@ -172,9 +174,9 @@ def _type_check(self, type_constraint, datum, is_input):
     try:
       check_constraint(type_constraint, datum)
     except CompositeTypeHintError as e:
-      raise TypeCheckError, e.message, sys.exc_info()[2]
+      six.reraise(TypeCheckError, e.message, sys.exc_info()[2])
     except SimpleTypeHintError:
       error_msg = ("According to type-hint expected %s should be of type %s. "
                    "Instead, received '%s', an instance of type %s."
                    % (datum_type, type_constraint, datum, type(datum)))
-      raise TypeCheckError, error_msg, sys.exc_info()[2]
+      six.reraise(TypeCheckError, error_msg, sys.exc_info()[2])
diff --git a/sdks/python/apache_beam/typehints/typed_pipeline_test.py 
b/sdks/python/apache_beam/typehints/typed_pipeline_test.py
index 2581457e7ea..f92dc61f1c1 100644
--- a/sdks/python/apache_beam/typehints/typed_pipeline_test.py
+++ b/sdks/python/apache_beam/typehints/typed_pipeline_test.py
@@ -16,6 +16,7 @@
 #
 
 """Unit tests for the type-hint objects and decorators."""
+from __future__ import absolute_import
 import inspect
 import typing
 import unittest
@@ -60,8 +61,8 @@ def test_non_function(self):
       [1, 2, 3] | beam.Map(str.upper)
 
   def test_loose_bounds(self):
-    @typehints.with_input_types(typehints.Union[int, float, long])
-    @typehints.with_output_types(basestring)
+    @typehints.with_input_types(typehints.Union[int, float, int])
+    @typehints.with_output_types(str)
     def format_number(x):
       return '%g' % x
     result = [1, 2, 3] | beam.Map(format_number)
diff --git a/sdks/python/apache_beam/typehints/typehints.py 
b/sdks/python/apache_beam/typehints/typehints.py
index 6e1d8b7f276..f3a52904c60 100644
--- a/sdks/python/apache_beam/typehints/typehints.py
+++ b/sdks/python/apache_beam/typehints/typehints.py
@@ -63,10 +63,12 @@
 
 """
 
+from __future__ import absolute_import
 import collections
 import copy
 import sys
-import types
+import six
+from six.moves import zip
 
 __all__ = [
     'Any',
@@ -335,7 +337,7 @@ def validate_composite_type_param(type_param, 
error_msg_prefix):
   # Must either be a TypeConstraint instance or a basic Python type.
   possible_classes = [type, TypeConstraint]
   if sys.version_info[0] == 2:
-    possible_classes.append(types.ClassType)
+    possible_classes.append(type)
   is_not_type_constraint = (
       not isinstance(type_param, tuple(possible_classes))
       and type_param is not None)
@@ -802,7 +804,7 @@ def type_check(self, dict_instance):
             'type dict. %s is of type %s.'
             % (dict_instance, dict_instance.__class__.__name__))
 
-      for key, value in dict_instance.iteritems():
+      for key, value in six.iteritems(dict_instance):
         try:
           check_constraint(self.key_type, key)
         except CompositeTypeHintError as e:
@@ -985,7 +987,8 @@ def __getitem__(self, type_param):
 IteratorTypeConstraint = IteratorHint.IteratorTypeConstraint
 
 
-class WindowedTypeConstraint(TypeConstraint):
+class WindowedTypeConstraint(six.with_metaclass(GetitemConstructor,
+                                                TypeConstraint)):
   """A type constraint for WindowedValue objects.
 
   Mostly for internal use.
@@ -993,7 +996,6 @@ class WindowedTypeConstraint(TypeConstraint):
   Attributes:
     inner_type: The type which the element should be an instance of.
   """
-  __metaclass__ = GetitemConstructor
 
   def __init__(self, inner_type):
     self.inner_type = inner_type
diff --git a/sdks/python/apache_beam/typehints/typehints_test.py 
b/sdks/python/apache_beam/typehints/typehints_test.py
index af575f4aba4..f7376f87902 100644
--- a/sdks/python/apache_beam/typehints/typehints_test.py
+++ b/sdks/python/apache_beam/typehints/typehints_test.py
@@ -16,10 +16,13 @@
 #
 
 """Unit tests for the type-hint objects and decorators."""
+from __future__ import absolute_import
 import functools
 import inspect
 import unittest
 
+from six.moves import range
+
 import apache_beam.typehints.typehints as typehints
 from apache_beam.typehints import Any
 from apache_beam.typehints import Tuple
@@ -134,7 +137,7 @@ def 
test_getitem_must_be_valid_type_param_cant_be_object_instance(self):
     with self.assertRaises(TypeError) as e:
       typehints.Union[5]
     self.assertEqual('Cannot create Union without a sequence of types.',
-                     e.exception.message)
+                     str(e.exception))
 
   def test_getitem_must_be_valid_type_param(self):
     t = [2, 3]
@@ -142,7 +145,7 @@ def test_getitem_must_be_valid_type_param(self):
       typehints.Union[t]
     self.assertEqual('All parameters to a Union hint must be a non-sequence, '
                      'a type, or a TypeConstraint. 2 is an instance of int.',
-                     e.exception.message)
+                     str(e.exception))
 
   def test_getitem_duplicates_ignored(self):
     # Types should be de-duplicated.
@@ -219,7 +222,7 @@ def test_union_hint_enforcement_not_part_of_union(self):
     self.assertEqual("Union[float, int] type-constraint violated. Expected an "
                      "instance of one of: ('float', 'int'), received str "
                      "instead.",
-                     e.exception.message)
+                     str(e.exception))
 
 
 class OptionalHintTestCase(TypeHintTestCase):
@@ -227,7 +230,7 @@ class OptionalHintTestCase(TypeHintTestCase):
   def test_getitem_sequence_not_allowed(self):
     with self.assertRaises(TypeError) as e:
       typehints.Optional[int, str]
-    self.assertTrue(e.exception.message.startswith(
+    self.assertTrue(str(e.exception).startswith(
         'An Option type-hint only accepts a single type parameter.'))
 
   def test_getitem_proxy_to_union(self):
@@ -243,21 +246,21 @@ def test_getitem_invalid_ellipsis_type_param(self):
 
     with self.assertRaises(TypeError) as e:
       typehints.Tuple[int, int, ...]
-    self.assertEqual(error_msg, e.exception.message)
+    self.assertEqual(error_msg, str(e.exception))
 
     with self.assertRaises(TypeError) as e:
       typehints.Tuple[...]
-    self.assertEqual(error_msg, e.exception.message)
+    self.assertEqual(error_msg, str(e.exception))
 
   def test_getitem_params_must_be_type_or_constraint(self):
     expected_error_prefix = 'All parameters to a Tuple hint must be'
     with self.assertRaises(TypeError) as e:
       typehints.Tuple[5, [1, 3]]
-    self.assertTrue(e.exception.message.startswith(expected_error_prefix))
+    self.assertTrue(str(e.exception).startswith(expected_error_prefix))
 
     with self.assertRaises(TypeError) as e:
       typehints.Tuple[list, dict]
-    self.assertTrue(e.exception.message.startswith(expected_error_prefix))
+    self.assertTrue(str(e.exception).startswith(expected_error_prefix))
 
   def test_compatibility_arbitrary_length(self):
     self.assertNotCompatible(
@@ -310,7 +313,7 @@ def test_type_check_must_be_tuple(self):
     for t in invalid_instances:
       with self.assertRaises(TypeError) as e:
         hint.type_check(t)
-      self.assertTrue(e.exception.message.startswith(expected_error_prefix))
+      self.assertTrue(str(e.exception).startswith(expected_error_prefix))
 
   def test_type_check_must_have_same_arity(self):
     # A 2-tuple of ints.
@@ -322,7 +325,7 @@ def test_type_check_must_have_same_arity(self):
     self.assertEqual('Passed object instance is of the proper type, but '
                      'differs in length from the hinted type. Expected a '
                      'tuple of length 2, received a tuple of length 3.',
-                     e.exception.message)
+                     str(e.exception))
 
   def test_type_check_invalid_simple_types(self):
     hint = typehints.Tuple[str, bool]
@@ -332,7 +335,7 @@ def test_type_check_invalid_simple_types(self):
                      'type of element #0 in the passed tuple is incorrect.'
                      ' Expected an instance of type str, instead received '
                      'an instance of type int.',
-                     e.exception.message)
+                     str(e.exception))
 
   def test_type_check_invalid_composite_type(self):
     hint = typehints.Tuple[DummyTestClass1, DummyTestClass2]
@@ -345,7 +348,7 @@ def test_type_check_invalid_composite_type(self):
                      'passed tuple is incorrect. Expected an instance of type '
                      'DummyTestClass1, instead received an instance of type '
                      'DummyTestClass2.',
-                     e.exception.message)
+                     str(e.exception))
 
   def test_type_check_valid_simple_types(self):
     hint = typehints.Tuple[float, bool]
@@ -382,7 +385,7 @@ def 
test_type_check_invalid_simple_type_arbitrary_length(self):
                      'of element #2 in the passed tuple is incorrect. Expected 
'
                      'an instance of type str, instead received an instance of 
'
                      'type int.',
-                     e.exception.message)
+                     str(e.exception))
 
   def test_type_check_invalid_composite_type_arbitrary_length(self):
     hint = typehints.Tuple[typehints.List[int], ...]
@@ -396,7 +399,7 @@ def 
test_type_check_invalid_composite_type_arbitrary_length(self):
                      "List type-constraint violated. Valid object instance "
                      "must be of type 'list'. Instead, an instance of 'str' "
                      "was received.",
-                     e.exception.message)
+                     str(e.exception))
 
 
 class ListHintTestCase(TypeHintTestCase):
@@ -439,7 +442,7 @@ def 
test_enforce_list_type_constraint_invalid_simple_type(self):
                      'element #0 in the passed list is incorrect. Expected an '
                      'instance of type int, instead received an instance of '
                      'type str.',
-                     e.exception.message)
+                     str(e.exception))
 
   def test_enforce_list_type_constraint_invalid_composite_type(self):
     hint = typehints.List[typehints.Tuple[int, int]]
@@ -453,7 +456,7 @@ def 
test_enforce_list_type_constraint_invalid_composite_type(self):
                      'violated. The type of element #0 in the passed tuple'
                      ' is incorrect. Expected an instance of type int, '
                      'instead received an instance of type str.',
-                     e.exception.message)
+                     str(e.exception))
 
 
 class KVHintTestCase(TypeHintTestCase):
@@ -464,16 +467,16 @@ def test_getitem_param_must_be_tuple(self):
 
     self.assertEqual('Parameter to KV type-hint must be a tuple of types: '
                      'KV[.., ..].',
-                     e.exception.message)
+                     str(e.exception))
 
   def test_getitem_param_must_have_length_2(self):
     with self.assertRaises(TypeError) as e:
       typehints.KV[int, str, bool]
 
     self.assertEqual("Length of parameters to a KV type-hint must be "
-                     "exactly 2. Passed parameters: (<type 'int'>, <type "
-                     "'str'>, <type 'bool'>), have a length of 3.",
-                     e.exception.message)
+                     "exactly 2. Passed parameters: ({}, {}, {}), "
+                     "have a length of 3.".format(int, str, bool),
+                     str(e.exception))
 
   def test_getitem_proxy_to_tuple(self):
     hint = typehints.KV[int, str]
@@ -493,16 +496,16 @@ def test_getitem_param_must_be_tuple(self):
 
     self.assertEqual('Parameter to Dict type-hint must be a tuple of '
                      'types: Dict[.., ..].',
-                     e.exception.message)
+                     str(e.exception))
 
   def test_getitem_param_must_have_length_2(self):
     with self.assertRaises(TypeError) as e:
       typehints.Dict[float, int, bool]
 
     self.assertEqual("Length of parameters to a Dict type-hint must be "
-                     "exactly 2. Passed parameters: (<type 'float'>, <type "
-                     "'int'>, <type 'bool'>), have a length of 3.",
-                     e.exception.message)
+                     "exactly 2. Passed parameters: ({}, {}, {}), "
+                     "have a length of 3.".format(float, int, bool),
+                     str(e.exception))
 
   def test_key_type_must_be_valid_composite_param(self):
     with self.assertRaises(TypeError):
@@ -533,7 +536,7 @@ def test_type_checks_not_dict(self):
       hint.type_check(l)
     self.assertEqual('Dict type-constraint violated. All passed instances '
                      'must be of type dict. [1, 2] is of type list.',
-                     e.exception.message)
+                     str(e.exception))
 
   def test_type_check_invalid_key_type(self):
     hint = typehints.Dict[typehints.Tuple[int, int, int],
@@ -548,7 +551,7 @@ def test_type_check_invalid_key_type(self):
                      'instance is of the proper type, but differs in '
                      'length from the hinted type. Expected a tuple of '
                      'length 3, received a tuple of length 2.',
-                     e.exception.message)
+                     str(e.exception))
 
   def test_type_check_invalid_value_type(self):
     hint = typehints.Dict[str, typehints.Dict[int, str]]
@@ -560,7 +563,7 @@ def test_type_check_invalid_value_type(self):
                      'Dict[int, str]. Instead: Dict type-constraint '
                      'violated. All passed instances must be of type dict.'
                      ' [1, 2, 3] is of type list.',
-                     e.exception.message)
+                     str(e.exception))
 
   def test_type_check_valid_simple_type(self):
     hint = typehints.Dict[int, str]
@@ -586,9 +589,9 @@ def test_getitem_invalid_composite_type_param(self):
     with self.assertRaises(TypeError) as e:
       typehints.Set[list]
     self.assertEqual("Parameter to a Set hint must be a non-sequence, a "
-                     "type, or a TypeConstraint. <type 'list'> is an "
-                     "instance of type.",
-                     e.exception.message)
+                     "type, or a TypeConstraint. {} is an "
+                     "instance of type.".format(list),
+                     str(e.exception))
 
   def test_compatibility(self):
     hint1 = typehints.Set[typehints.List[str]]
@@ -609,7 +612,7 @@ def test_type_check_must_be_set(self):
     self.assertEqual("Set type-constraint violated. Valid object instance "
                      "must be of type 'set'. Instead, an instance of 'int'"
                      " was received.",
-                     e.exception.message)
+                     str(e.exception))
 
   def test_type_check_invalid_elem_type(self):
     hint = typehints.Set[float]
@@ -635,7 +638,7 @@ def test_getitem_invalid_composite_type_param(self):
     self.assertEqual('Parameter to an Iterable hint must be a '
                      'non-sequence, a type, or a TypeConstraint. 5 is '
                      'an instance of int.',
-                     e.exception.message)
+                     str(e.exception))
 
   def test_compatibility(self):
     self.assertCompatible(typehints.Iterable[int], typehints.List[int])
@@ -678,7 +681,7 @@ def test_type_check_must_be_iterable(self):
     self.assertEqual("Iterable type-constraint violated. Valid object "
                      "instance must be of type 'iterable'. Instead, an "
                      "instance of 'int' was received.",
-                     e.exception.message)
+                     str(e.exception))
 
   def test_type_check_violation_invalid_simple_type(self):
     hint = typehints.Iterable[float]
@@ -746,7 +749,7 @@ def all_upper(s):
                      'hint type-constraint violated. Expected a iterator '
                      'of type int. Instead received a iterator of type '
                      'str.',
-                     e.exception.message)
+                     str(e.exception))
 
   def test_generator_argument_hint_invalid_yield_type(self):
     def wrong_yield_gen():
@@ -765,7 +768,7 @@ def increment(a):
                      "hint type-constraint violated. Expected a iterator "
                      "of type int. Instead received a iterator of type "
                      "str.",
-                     e.exception.message)
+                     str(e.exception))
 
 
 class TakesDecoratorTestCase(TypeHintTestCase):
@@ -781,7 +784,7 @@ def unused_foo(a):
     self.assertEqual('All type hint arguments must be a non-sequence, a '
                      'type, or a TypeConstraint. [1, 2] is an instance of '
                      'list.',
-                     e.exception.message)
+                     str(e.exception))
 
     with self.assertRaises(TypeError) as e:
       t = 5
@@ -793,7 +796,7 @@ def unused_foo(a):
 
     self.assertEqual('All type hint arguments must be a non-sequence, a type, '
                      'or a TypeConstraint. 5 is an instance of int.',
-                     e.exception.message)
+                     str(e.exception))
 
   def test_basic_type_assertion(self):
     @check_type_hints
@@ -805,9 +808,9 @@ def foo(a):
       m = 'a'
       foo(m)
     self.assertEqual("Type-hint for argument: 'a' violated. Expected an "
-                     "instance of <type 'int'>, instead found an "
-                     "instance of <type 'str'>.",
-                     e.exception.message)
+                     "instance of {}, instead found an "
+                     "instance of {}.".format(int, str),
+                     str(e.exception))
 
   def test_composite_type_assertion(self):
     @check_type_hints
@@ -823,7 +826,7 @@ def foo(a):
                        "type-constraint violated. The type of element #0 in "
                        "the passed list is incorrect. Expected an instance of "
                        "type int, instead received an instance of type str.",
-                       e.exception.message)
+                       str(e.exception))
 
   def test_valid_simple_type_arguments(self):
     @with_input_types(a=str)
@@ -859,9 +862,9 @@ def sub(a, b):
       sub(1, 'two')
 
     self.assertEqual("Type-hint for argument: 'b' violated. Expected an "
-                     "instance of <type 'int'>, instead found an instance "
-                     "of <type 'str'>.",
-                     e.exception.message)
+                     "instance of {}, instead found an instance "
+                     "of {}.".format(int, str),
+                     str(e.exception))
 
   def test_valid_only_positional_arguments(self):
     @with_input_types(int, int)
@@ -905,9 +908,9 @@ def foo(a):
       foo(4)
 
     self.assertEqual("Type-hint for return type violated. Expected an "
-                     "instance of <type 'int'>, instead found an instance "
-                     "of <type 'str'>.",
-                     e.exception.message)
+                     "instance of {}, instead found an instance "
+                     "of {}.".format(int, str),
+                     str(e.exception))
 
   def test_type_check_simple_type(self):
     @with_output_types(str)
@@ -1046,15 +1049,15 @@ def test_positional_arg_hints(self):
                       _positional_arg_hints(['x', 'y'], {'x': int}))
 
   def test_getcallargs_forhints(self):
-    func = lambda a, (b, c), *d: None
+    func = lambda a, b_c, *d: None
     self.assertEquals(
-        {'a': Any, 'b': Any, 'c': Any, 'd': Tuple[Any, ...]},
-        getcallargs_forhints(func, *[Any, Any]))
+        {'a': Any, 'b_c': Tuple[Any, Any], 'd': Tuple[Any, ...]},
+        getcallargs_forhints(func, *[Any, Tuple[Any, Any]]))
     self.assertEquals(
-        {'a': Any, 'b': Any, 'c': Any, 'd': Tuple[Any, ...]},
-        getcallargs_forhints(func, *[Any, Any, Any, int]))
+        {'a': Any, 'b_c': Tuple[Any, Any], 'd': Tuple[Any, ...]},
+        getcallargs_forhints(func, *[Any, Tuple[Any, Any], int]))
     self.assertEquals(
-        {'a': int, 'b': str, 'c': Any, 'd': Tuple[Any, ...]},
+        {'a': int, 'b_c': Tuple[str, Any], 'd': Tuple[Any, ...]},
         getcallargs_forhints(func, *[int, Tuple[str, Any]]))
 
 


 

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
us...@infra.apache.org


> Fix type inference in Python 3 for generators
> ---------------------------------------------
>
>                 Key: BEAM-3143
>                 URL: https://issues.apache.org/jira/browse/BEAM-3143
>             Project: Beam
>          Issue Type: Sub-task
>          Components: sdk-py-core
>            Reporter: holdenk
>




--
This message was sent by Atlassian JIRA
(v6.4.14#64029)

Reply via email to