[
https://issues.apache.org/jira/browse/BEAM-3143?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=16295485#comment-16295485
]
ASF GitHub Bot commented on BEAM-3143:
--------------------------------------
robertwb closed pull request #4183: [BEAM-3143] Type Inference Python 3
Compatibility
URL: https://github.com/apache/beam/pull/4183
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/opcodes.py
b/sdks/python/apache_beam/typehints/opcodes.py
index dcca6d02644..ccf0195a58f 100644
--- a/sdks/python/apache_beam/typehints/opcodes.py
+++ b/sdks/python/apache_beam/typehints/opcodes.py
@@ -28,9 +28,13 @@
"""
from __future__ import absolute_import
+import inspect
+import sys
import types
from functools import reduce
+import six
+
from . import typehints
from .trivial_inference import BoundMethod
from .trivial_inference import Const
@@ -147,7 +151,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)
@@ -261,13 +265,22 @@ def build_list(state, arg):
def load_attr(state, arg):
+ """Replaces the top of the stack, TOS, with
+ getattr(TOS, co_names[arg])
+ """
o = state.stack.pop()
name = state.get_name(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)):
- state.stack.append(Const(BoundMethod(getattr(o, name))))
+ elif (inspect.isclass(o) and
+ isinstance(getattr(o, name, None),
+ (types.MethodType, types.FunctionType))):
+ # TODO(luke-zhu): Support other callable objects
+ if sys.version_info[0] == 2:
+ func = getattr(o, name).__func__
+ else:
+ func = getattr(o, name) # Python 3 has no unbound methods
+ state.stack.append(Const(BoundMethod(func, o)))
else:
state.stack.append(Any)
@@ -327,7 +340,18 @@ 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 with the arguments at the top of the stack.
+ """
+ # TODO(luke-zhu): Handle default argument types
+ globals = state.f.__globals__ # Inherits globals from the current frame
+ if sys.version_info[0] == 2:
+ func_code = state.stack[-1].value
+ func = types.FunctionType(func_code, globals)
+ else:
+ func_name = state.stack[-1].value
+ func_code = state.stack[-2].value
+ func = types.FunctionType(func_code, globals, name=func_name)
+ state.stack.append(Const(func))
def make_closure(state, arg):
diff --git a/sdks/python/apache_beam/typehints/trivial_inference.py
b/sdks/python/apache_beam/typehints/trivial_inference.py
index a68bd18b1c3..28bf8f5ba6f 100644
--- a/sdks/python/apache_beam/typehints/trivial_inference.py
+++ b/sdks/python/apache_beam/typehints/trivial_inference.py
@@ -22,9 +22,9 @@
from __future__ import absolute_import
from __future__ import print_function
-import __builtin__
import collections
import dis
+import inspect
import pprint
import sys
import types
@@ -32,6 +32,8 @@
from apache_beam.typehints import Any
from apache_beam.typehints import typehints
+from six.moves import builtins
+from six.moves import zip
class TypeInferenceError(ValueError):
@@ -46,9 +48,9 @@ def instance_to_type(o):
if o is None:
return type(None)
elif t not in typehints.DISALLOWED_PRIMITIVE_TYPES:
- if t == types.InstanceType:
+ if sys.version_info[0] == 2 and t == types.InstanceType:
return o.__class__
- elif t == BoundMethod:
+ if t == BoundMethod:
return types.MethodType
return t
elif t == tuple:
@@ -107,15 +109,12 @@ 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__
+ return isinstance(other, FrameState) and self.__dict__ == other.__dict__
def copy(self):
return FrameState(self.f, self.vars, self.stack)
@@ -133,8 +132,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):
@@ -203,8 +202,15 @@ class BoundMethod(object):
"""Used to create a bound method when we only know the type of the instance.
"""
- def __init__(self, unbound):
- self.unbound = unbound
+ def __init__(self, func, type):
+ """Instantiates a bound method object.
+
+ Args:
+ func (types.FunctionType): The method's underlying function
+ type (type): The class of the method.
+ """
+ self.func = func
+ self.type = type
def hashable(c):
@@ -238,10 +244,9 @@ def infer_return_type(c, input_types, debug=False,
depth=5):
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):
+ input_types = [c.type] + input_types
+ return infer_return_type_func(c.func, input_types, debug, depth)
+ elif inspect.isclass(c):
if c in typehints.DISALLOWED_PRIMITIVE_TYPES:
return {
list: typehints.List[Any],
@@ -303,15 +308,23 @@ def infer_return_type_func(f, input_types, debug=False,
depth=0):
last_pc = -1
while pc < end:
start = pc
- op = ord(code[pc])
-
+ if sys.version_info[0] == 2:
+ op = ord(code[pc])
+ else:
+ op = code[pc]
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
+ if sys.version_info[0] == 2:
+ arg = ord(code[pc]) + ord(code[pc + 1]) * 256 + extended_arg
+ elif sys.version_info[0] == 3 and sys.version_info[1] < 6:
+ arg = code[pc] + code[pc + 1] * 256 + extended_arg
+ else:
+ pass # TODO(luke-zhu): Python 3.6 bytecode to wordcode changes
extended_arg = 0
pc += 2
if op == dis.EXTENDED_ARG:
@@ -344,7 +357,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
+ 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
diff --git a/sdks/python/apache_beam/typehints/trivial_inference_test.py
b/sdks/python/apache_beam/typehints/trivial_inference_test.py
index 37b22584723..b017e8af497 100644
--- a/sdks/python/apache_beam/typehints/trivial_inference_test.py
+++ b/sdks/python/apache_beam/typehints/trivial_inference_test.py
@@ -72,6 +72,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: [1, 2],
+ [typehints.Tuple[int, ...]])
+
+ self.assertReturnType(
+ typehints.List[typehints.Any],
+ lambda xs: list(xs), # List is a disallowed builtin
+ [typehints.Tuple[int, ...]])
+
def testListComprehension(self):
self.assertReturnType(
typehints.List[int],
@@ -98,6 +109,12 @@ def foo(x, y):
self.assertReturnType(
typehints.Iterable[typehints.Union[int, float]], foo, [int, float])
+ def testGeneratorComprehension(self):
+ self.assertReturnType(
+ typehints.Iterable[int],
+ lambda xs: (x for x in xs),
+ [typehints.Tuple[int, ...]])
+
def testBinOp(self):
self.assertReturnType(int, lambda a, b: a + b, [int, int])
self.assertReturnType(
@@ -130,7 +147,6 @@ def testGetAttr(self):
def testMethod(self):
class A(object):
-
def m(self, x):
return x
----------------------------------------------------------------
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:
[email protected]
> 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)