Commit 3dc2b9df authored by da-woods's avatar da-woods Committed by Stefan Behnel

Implement PEP-563, annotations as strings (GH-3285)

Annotations are now dealt with according to PEP-563 - they are
saved as strings, rather than evaluated as Python objects.
They can/are still be used by Cython for typing.
Previous behaviour for evaluating them as Python objects was
convoluted and has been removed entirely, which hopefully doesn't
break too much.
parent acf3abf8
......@@ -5,16 +5,35 @@ from .StringEncoding import EncodedString
from . import Options
from . import PyrexTypes, ExprNodes
from ..CodeWriter import ExpressionWriter
from .Errors import warning
class AnnotationWriter(ExpressionWriter):
def __init__(self, description=None):
"""description is optional. If specified it is used in
warning messages for the nodes that don't convert to string properly.
If not specified then no messages are generated.
"""
ExpressionWriter.__init__(self)
self.description = description
def visit_Node(self, node):
self.put(u"<???>")
if self.description:
warning(node.pos,
"Failed to convert code to string representation in {0}".format(
self.description), level=1)
def visit_LambdaNode(self, node):
# XXX Should we do better?
self.put("<lambda>")
if self.description:
warning(node.pos,
"Failed to convert lambda to string representation in {0}".format(
self.description), level=1)
def visit_AnnotationNode(self, node):
self.put(node.string.unicode_value)
class EmbedSignature(CythonTransform):
......
......@@ -28,7 +28,7 @@ from .Code import UtilityCode, TempitaUtilityCode
from . import StringEncoding
from . import Naming
from . import Nodes
from .Nodes import Node, utility_code_for_imports, analyse_type_annotation
from .Nodes import Node, utility_code_for_imports
from . import PyrexTypes
from .PyrexTypes import py_object_type, c_long_type, typecast, error_type, \
unspecified_type
......@@ -1928,15 +1928,15 @@ class NameNode(AtomicExprNode):
return
annotation = self.annotation
if annotation.is_string_literal:
if annotation.expr.is_string_literal:
# name: "description" => not a type, but still a declared variable or attribute
atype = None
else:
_, atype = analyse_type_annotation(annotation, env)
_, atype = annotation.analyse_type_annotation(env)
if atype is None:
atype = unspecified_type if as_target and env.directives['infer_types'] != False else py_object_type
self.entry = env.declare_var(name, atype, self.pos, is_cdef=not as_target)
self.entry.annotation = annotation
self.entry.annotation = annotation.expr
def analyse_as_module(self, env):
# Try to interpret this as a reference to a cimported module.
......@@ -9221,19 +9221,19 @@ class PyCFunctionNode(ExprNode, ModuleNameMixin):
else:
default_args.append(arg)
if arg.annotation:
arg.annotation = self.analyse_annotation(env, arg.annotation)
annotations.append((arg.pos, arg.name, arg.annotation))
arg.annotation = arg.annotation.analyse_types(env)
annotations.append((arg.pos, arg.name, arg.annotation.string))
for arg in (self.def_node.star_arg, self.def_node.starstar_arg):
if arg and arg.annotation:
arg.annotation = self.analyse_annotation(env, arg.annotation)
annotations.append((arg.pos, arg.name, arg.annotation))
arg.annotation = arg.annotation.analyse_types(env)
annotations.append((arg.pos, arg.name, arg.annotation.string))
annotation = self.def_node.return_type_annotation
if annotation:
annotation = self.analyse_annotation(env, annotation)
self.def_node.return_type_annotation = annotation
annotations.append((annotation.pos, StringEncoding.EncodedString("return"), annotation))
self.def_node.return_type_annotation = annotation.analyse_types(env)
annotations.append((annotation.pos, StringEncoding.EncodedString("return"),
annotation.string))
if nonliteral_objects or nonliteral_other:
module_scope = env.global_scope()
......@@ -9310,20 +9310,6 @@ class PyCFunctionNode(ExprNode, ModuleNameMixin):
for pos, name, value in annotations])
self.annotations_dict = annotations_dict.analyse_types(env)
def analyse_annotation(self, env, annotation):
if annotation is None:
return None
atype = annotation.analyse_as_type(env)
if atype is not None:
# Keep parsed types as strings as they might not be Python representable.
annotation = UnicodeNode(
annotation.pos,
value=StringEncoding.EncodedString(atype.declaration_code('', for_display=True)))
annotation = annotation.analyse_types(env)
if not annotation.type.is_pyobject:
annotation = annotation.coerce_to_pyobject(env)
return annotation
def may_be_none(self):
return False
......@@ -12015,6 +12001,9 @@ class BoolBinopResultNode(ExprNode):
code.putln("}")
self.arg.free_temps(code)
def analyse_types(self, env):
return self
class CondExprNode(ExprNode):
# Short-circuiting conditional expression.
......@@ -13330,6 +13319,9 @@ class CoerceToBooleanNode(CoercionNode):
self.arg.py_result(),
code.error_goto_if_neg(self.result(), self.pos)))
def analyse_types(self, env):
return self
class CoerceToComplexNode(CoercionNode):
......@@ -13355,6 +13347,9 @@ class CoerceToComplexNode(CoercionNode):
def generate_result_code(self, code):
pass
def analyse_types(self, env):
return self
class CoerceToTempNode(CoercionNode):
# This node is used to force the result of another node
# to be stored in a temporary. It is only used if the
......@@ -13560,7 +13555,83 @@ class DocstringRefNode(ExprNode):
code.error_goto_if_null(self.result(), self.pos)))
code.put_gotref(self.result())
class AnnotationNode(ExprNode):
# Deals with the two possible uses of an annotation.
# 1. The post PEP-563 use where an annotation is stored
# as a string
# 2. The Cython use where the annotation can indicate an
# object type
#
# doesn't handle the pre PEP-563 version where the
# annotation is evaluated into a Python Object
subexprs = []
def __init__(self, pos, expr, string=None):
"""string is expected to already be a StringNode or None"""
ExprNode.__init__(self, pos)
if string is None:
# import doesn't work at top of file?
from .AutoDocTransforms import AnnotationWriter
string = StringEncoding.EncodedString(
AnnotationWriter(description="annotation").write(expr))
string = StringNode(pos, unicode_value=string, value=string.as_utf8_string())
self.string = string
self.expr = expr
def analyse_types(self, env):
return self # nothing needs doing
def analyse_as_type(self, env):
# for compatibility when used as a return_type_node, have this interface too
return self.analyse_type_annotation(env)[1]
def analyse_type_annotation(self, env, assigned_value=None):
annotation = self.expr
base_type = None
is_ambiguous = False
explicit_pytype = explicit_ctype = False
if annotation.is_dict_literal:
warning(annotation.pos,
"Dicts should no longer be used as type annotations. Use 'cython.int' etc. directly.")
for name, value in annotation.key_value_pairs:
if not name.is_string_literal:
continue
if name.value in ('type', b'type'):
explicit_pytype = True
if not explicit_ctype:
annotation = value
elif name.value in ('ctype', b'ctype'):
explicit_ctype = True
annotation = value
if explicit_pytype and explicit_ctype:
warning(annotation.pos, "Duplicate type declarations found in signature annotation")
arg_type = annotation.analyse_as_type(env)
if annotation.is_name and not annotation.cython_attribute and annotation.name in ('int', 'long', 'float'):
# Map builtin numeric Python types to C types in safe cases.
if assigned_value is not None and arg_type is not None and not arg_type.is_pyobject:
assigned_type = assigned_value.infer_type(env)
if assigned_type and assigned_type.is_pyobject:
# C type seems unsafe, e.g. due to 'None' default value => ignore annotation type
is_ambiguous = True
arg_type = None
# ignore 'int' and require 'cython.int' to avoid unsafe integer declarations
if arg_type in (PyrexTypes.c_long_type, PyrexTypes.c_int_type, PyrexTypes.c_float_type):
arg_type = PyrexTypes.c_double_type if annotation.name == 'float' else py_object_type
elif arg_type is not None and annotation.is_string_literal:
warning(annotation.pos,
"Strings should no longer be used for type declarations. Use 'cython.int' etc. directly.",
level=1)
if arg_type is not None:
if explicit_pytype and not explicit_ctype and not arg_type.is_pyobject:
warning(annotation.pos,
"Python type declaration in signature annotation does not refer to a Python type")
base_type = Nodes.CAnalysedBaseTypeNode(
annotation.pos, type=arg_type, is_arg=True)
elif is_ambiguous:
warning(annotation.pos, "Ambiguous types in annotation, ignoring")
else:
warning(annotation.pos, "Unknown type declaration in annotation, ignoring")
return base_type, arg_type
#------------------------------------------------------------------------------------
#
......
......@@ -11,5 +11,6 @@ absolute_import = _get_feature("absolute_import")
nested_scopes = _get_feature("nested_scopes") # dummy
generators = _get_feature("generators") # dummy
generator_stop = _get_feature("generator_stop")
annotations = _get_feature("annotations")
del _get_feature
......@@ -68,54 +68,6 @@ def embed_position(pos, docstring):
doc.encoding = encoding
return doc
def analyse_type_annotation(annotation, env, assigned_value=None):
base_type = None
is_ambiguous = False
explicit_pytype = explicit_ctype = False
if annotation.is_dict_literal:
warning(annotation.pos,
"Dicts should no longer be used as type annotations. Use 'cython.int' etc. directly.")
for name, value in annotation.key_value_pairs:
if not name.is_string_literal:
continue
if name.value in ('type', b'type'):
explicit_pytype = True
if not explicit_ctype:
annotation = value
elif name.value in ('ctype', b'ctype'):
explicit_ctype = True
annotation = value
if explicit_pytype and explicit_ctype:
warning(annotation.pos, "Duplicate type declarations found in signature annotation")
arg_type = annotation.analyse_as_type(env)
if annotation.is_name and not annotation.cython_attribute and annotation.name in ('int', 'long', 'float'):
# Map builtin numeric Python types to C types in safe cases.
if assigned_value is not None and arg_type is not None and not arg_type.is_pyobject:
assigned_type = assigned_value.infer_type(env)
if assigned_type and assigned_type.is_pyobject:
# C type seems unsafe, e.g. due to 'None' default value => ignore annotation type
is_ambiguous = True
arg_type = None
# ignore 'int' and require 'cython.int' to avoid unsafe integer declarations
if arg_type in (PyrexTypes.c_long_type, PyrexTypes.c_int_type, PyrexTypes.c_float_type):
arg_type = PyrexTypes.c_double_type if annotation.name == 'float' else py_object_type
elif arg_type is not None and annotation.is_string_literal:
warning(annotation.pos,
"Strings should no longer be used for type declarations. Use 'cython.int' etc. directly.")
if arg_type is not None:
if explicit_pytype and not explicit_ctype and not arg_type.is_pyobject:
warning(annotation.pos,
"Python type declaration in signature annotation does not refer to a Python type")
base_type = CAnalysedBaseTypeNode(
annotation.pos, type=arg_type, is_arg=True)
elif is_ambiguous:
warning(annotation.pos, "Ambiguous types in annotation, ignoring")
else:
warning(annotation.pos, "Unknown type declaration in annotation, ignoring")
return base_type, arg_type
def write_func_call(func, codewriter_class):
def f(*args, **kwds):
if len(args) > 1 and isinstance(args[1], codewriter_class):
......@@ -937,7 +889,7 @@ class CArgDeclNode(Node):
annotation = self.annotation
if not annotation:
return None
base_type, arg_type = analyse_type_annotation(annotation, env, assigned_value=self.default)
base_type, arg_type = annotation.analyse_type_annotation(env, assigned_value=self.default)
if base_type is not None:
self.base_type = base_type
return arg_type
......@@ -1685,6 +1637,7 @@ class FuncDefNode(StatNode, BlockNode):
starstar_arg = None
is_cyfunction = False
code_object = None
return_type_annotation = None
def analyse_default_values(self, env):
default_seen = 0
......@@ -1702,18 +1655,12 @@ class FuncDefNode(StatNode, BlockNode):
elif default_seen:
error(arg.pos, "Non-default argument following default argument")
def analyse_annotation(self, env, annotation):
# Annotations can not only contain valid Python expressions but arbitrary type references.
if annotation is None:
return None
if not env.directives['annotation_typing'] or annotation.analyse_as_type(env) is None:
annotation = annotation.analyse_types(env)
return annotation
def analyse_annotations(self, env):
for arg in self.args:
if arg.annotation:
arg.annotation = self.analyse_annotation(env, arg.annotation)
arg.annotation = arg.annotation.analyse_types(env)
if self.return_type_annotation:
self.return_type_annotation = self.return_type_annotation.analyse_types(env)
def align_argument_type(self, env, arg):
# @cython.locals()
......@@ -2208,7 +2155,7 @@ class FuncDefNode(StatNode, BlockNode):
error(arg.pos, "Argument type '%s' is incomplete" % arg.type)
entry = env.declare_arg(arg.name, arg.type, arg.pos)
if arg.annotation:
entry.annotation = arg.annotation
entry.annotation = arg.annotation.expr
return entry
def generate_arg_type_test(self, arg, code):
......@@ -2947,7 +2894,7 @@ class DefNode(FuncDefNode):
# if a signature annotation provides a more specific return object type, use it
if self.return_type is py_object_type and self.return_type_annotation:
if env.directives['annotation_typing'] and not self.entry.is_special:
_, return_type = analyse_type_annotation(self.return_type_annotation, env)
_, return_type = self.return_type_annotation.analyse_type_annotation(env)
if return_type and return_type.is_pyobject:
self.return_type = return_type
......@@ -3208,8 +3155,6 @@ class DefNode(FuncDefNode):
self.local_scope.directives = env.directives
self.analyse_default_values(env)
self.analyse_annotations(env)
if self.return_type_annotation:
self.return_type_annotation = self.analyse_annotation(env, self.return_type_annotation)
if not self.needs_assignment_synthesis(env) and self.decorators:
for decorator in self.decorators[::-1]:
......
......@@ -196,3 +196,4 @@ cdef dict p_compiler_directive_comments(PyrexScanner s)
cdef p_template_definition(PyrexScanner s)
cdef p_cpp_class_definition(PyrexScanner s, pos, ctx)
cdef p_cpp_class_attribute(PyrexScanner s, ctx)
cdef p_annotation(PyrexScanner s)
......@@ -1493,7 +1493,7 @@ def p_expression_or_assignment(s):
expr = p_testlist_star_expr(s)
if s.sy == ':' and (expr.is_name or expr.is_subscript or expr.is_attribute):
s.next()
expr.annotation = p_test(s)
expr.annotation = p_annotation(s)
if s.sy == '=' and expr.is_starred:
# This is a common enough error to make when learning Cython to let
# it fail as early as possible and give a very clear error message.
......@@ -3019,7 +3019,7 @@ def p_c_arg_decl(s, ctx, in_pyfunc, cmethod_flag = 0, nonempty = 0,
not_none = kind == 'not'
if annotated and s.sy == ':':
s.next()
annotation = p_test(s)
annotation = p_annotation(s)
if s.sy == '=':
s.next()
if 'pxd' in ctx.level:
......@@ -3406,7 +3406,7 @@ def p_def_statement(s, decorators=None, is_async_def=False):
return_type_annotation = None
if s.sy == '->':
s.next()
return_type_annotation = p_test(s)
return_type_annotation = p_annotation(s)
_reject_cdef_modifier_in_py(s, s.systring)
doc, body = p_suite_with_docstring(s, Ctx(level='function'))
......@@ -3461,7 +3461,7 @@ def p_py_arg_decl(s, annotated = 1):
annotation = None
if annotated and s.sy == ':':
s.next()
annotation = p_test(s)
annotation = p_annotation(s)
return Nodes.PyArgDeclNode(pos, name = name, annotation = annotation)
......@@ -3869,3 +3869,14 @@ def print_parse_tree(f, node, level, key = None):
f.write("%s]\n" % ind)
return
f.write("%s%s\n" % (ind, node))
def p_annotation(s):
"""An annotation just has the "test" syntax, but also stores the string it came from
Note that the string is *allowed* to be changed/processed (although isn't here)
so may not exactly match the string generated by Python, and if it doesn't
then it is not a bug.
"""
pos = s.position()
expr = p_test(s)
return ExprNodes.AnnotationNode(pos, expr=expr)
......@@ -656,7 +656,8 @@ class MemoryViewSliceType(PyrexType):
assert not pyrex
assert not dll_linkage
from . import MemoryView
base_code = str(self) if for_display else MemoryView.memviewslice_cname
base_code = StringEncoding.EncodedString(
(str(self)) if for_display else MemoryView.memviewslice_cname)
return self.base_declaration_code(
base_code,
entity_code)
......@@ -1767,6 +1768,7 @@ class CNumericType(CType):
base_code = type_name.replace('PY_LONG_LONG', 'long long')
else:
base_code = public_decl(type_name, dll_linkage)
base_code = StringEncoding.EncodedString(base_code)
return self.base_declaration_code(base_code, entity_code)
def attributes_known(self):
......
# mode: error
# tag: pep492, async
async def foo():
def foo(a:await list()):
pass
_ERRORS = """
5:14: 'await' not supported here
5:14: 'await' not supported here
"""
......@@ -271,13 +271,13 @@ def test_annotations(a: "test", b: "other" = 2, c: 123 = 4) -> "ret":
>>> isinstance(test_annotations.__annotations__, dict)
True
>>> sorted(test_annotations.__annotations__.items())
[('a', 'test'), ('b', 'other'), ('c', 123), ('return', 'ret')]
[('a', "'test'"), ('b', "'other'"), ('c', '123'), ('return', "'ret'")]
>>> def func_b(): return 42
>>> def func_c(): return 99
>>> inner = test_annotations(1, func_b, func_c)
>>> sorted(inner.__annotations__.items())
[('return', 99), ('x', 'banana'), ('y', 42)]
[('return', 'c()'), ('x', "'banana'"), ('y', 'b()')]
>>> inner.__annotations__ = {234: 567}
>>> inner.__annotations__
......@@ -293,14 +293,14 @@ def test_annotations(a: "test", b: "other" = 2, c: 123 = 4) -> "ret":
>>> inner = test_annotations(1, func_b, func_c)
>>> sorted(inner.__annotations__.items())
[('return', 99), ('x', 'banana'), ('y', 42)]
[('return', 'c()'), ('x', "'banana'"), ('y', 'b()')]
>>> inner.__annotations__['abc'] = 66
>>> sorted(inner.__annotations__.items())
[('abc', 66), ('return', 99), ('x', 'banana'), ('y', 42)]
[('abc', 66), ('return', 'c()'), ('x', "'banana'"), ('y', 'b()')]
>>> inner = test_annotations(1, func_b, func_c)
>>> sorted(inner.__annotations__.items())
[('return', 99), ('x', 'banana'), ('y', 42)]
[('return', 'c()'), ('x', "'banana'"), ('y', 'b()')]
"""
def inner(x: "banana", y: b()) -> c():
return x,y
......
......@@ -578,15 +578,15 @@ def annotation_syntax(a: "test new test", b : "other" = 2, *args: "ARGS", **kwar
>>> len(annotation_syntax.__annotations__)
5
>>> print(annotation_syntax.__annotations__['a'])
test new test
u'test new test'
>>> print(annotation_syntax.__annotations__['b'])
other
u'other'
>>> print(annotation_syntax.__annotations__['args'])
ARGS
u'ARGS'
>>> print(annotation_syntax.__annotations__['kwargs'])
KWARGS
u'KWARGS'
>>> print(annotation_syntax.__annotations__['return'])
ret
u'ret'
"""
result : int = a + b
......@@ -610,9 +610,9 @@ async def async_def_annotations(x: 'int') -> 'float':
>>> ret, arg = sorted(async_def_annotations.__annotations__.items())
>>> print(ret[0]); print(ret[1])
return
float
u'float'
>>> print(arg[0]); print(arg[1])
x
int
u'int'
"""
return float(x)
......@@ -140,6 +140,12 @@ def str_type_is_str():
cdef str s = 'abc'
return str, s
def strip_wrapped_string(s):
# PEP 563 translates an annotation of "test new test" to '"test new test"'
# but choice of string delimiters is a bit arbitrary
# this function handles that
assert s[0] == s[-1] # delimiters on either end are the same
return s[1:-1] # strip them
def annotation_syntax(a: "test new test", b : "other" = 2, *args: "ARGS", **kwargs: "KWARGS") -> "ret":
"""
......@@ -150,15 +156,15 @@ def annotation_syntax(a: "test new test", b : "other" = 2, *args: "ARGS", **kwar
>>> len(annotation_syntax.__annotations__)
5
>>> annotation_syntax.__annotations__['a']
>>> strip_wrapped_string(annotation_syntax.__annotations__['a'])
'test new test'
>>> annotation_syntax.__annotations__['b']
>>> strip_wrapped_string(annotation_syntax.__annotations__['b'])
'other'
>>> annotation_syntax.__annotations__['args']
>>> strip_wrapped_string(annotation_syntax.__annotations__['args'])
'ARGS'
>>> annotation_syntax.__annotations__['kwargs']
>>> strip_wrapped_string(annotation_syntax.__annotations__['kwargs'])
'KWARGS'
>>> annotation_syntax.__annotations__['return']
>>> strip_wrapped_string(annotation_syntax.__annotations__['return'])
'ret'
"""
result : int = a + b
......
#cython: embedsignature=True, annotation_typing=False
# signatures here are a little fragile - when they are
# generated during the build process gives slightly
# different (but equivalent) forms - therefore tests
# may need changing occasionally to reflect behaviour
# and this isn't necessarily a bug
import sys
if sys.version_info >= (3, 4):
def funcdoc(f):
if not f.__text_signature__:
if not getattr(f, "__text_signature__", None):
return f.__doc__
doc = '%s%s' % (f.__name__, f.__text_signature__)
if f.__doc__:
......@@ -447,10 +453,10 @@ Foo.m01(self, a: ...) -> Ellipsis
Foo.m02(self, a: True, b: False) -> bool
>>> print(Foo.m03.__doc__)
Foo.m03(self, a: 42, b: 42, c: -42) -> int
Foo.m03(self, a: 42, b: +42, c: -42) -> int
>>> print(Foo.m04.__doc__)
Foo.m04(self, a: 3.14, b: 3.14, c: -3.14) -> float
Foo.m04(self, a: 3.14, b: +3.14, c: -3.14) -> float
>>> print(Foo.m05.__doc__)
Foo.m05(self, a: 1 + 2j, b: +2j, c: -2j) -> complex
......@@ -465,7 +471,7 @@ Foo.m07(self, a: [1, 2, 3], b: []) -> list
Foo.m08(self, a: (1, 2, 3), b: ()) -> tuple
>>> print(Foo.m09.__doc__)
Foo.m09(self, a: {1, 2, 3}, b: set()) -> set
Foo.m09(self, a: {1, 2, 3}, b: {i for i in ()}) -> set
>>> print(Foo.m10.__doc__)
Foo.m10(self, a: {1: 1, 2: 2, 3: 3}, b: {}) -> dict
......
......@@ -30,10 +30,10 @@ def anno_gen(x: 'int') -> 'float':
>>> next(gen)
2.0
>>> ret, arg = sorted(anno_gen.__annotations__.items())
>>> print(ret[0]); print(ret[1])
>>> print(ret[0]); print(str(ret[1]).strip("'")) # strip makes it pass with/without PEP563
return
float
>>> print(arg[0]); print(arg[1])
>>> print(arg[0]); print(str(arg[1]).strip("'"))
x
int
"""
......
# mode: run
# tag: pep563, pure3.7
from __future__ import annotations
def f(a: 1+2==3, b: list, c: this_cant_evaluate, d: "Hello from inside a string") -> "Return me!":
"""
The absolute exact strings aren't reproducible according to the PEP,
so be careful to avoid being too specific
>>> stypes = (type(""), type(u"")) # Python 2 is a bit awkward here
>>> eval(f.__annotations__['a'])
True
>>> isinstance(f.__annotations__['a'], stypes)
True
>>> print(f.__annotations__['b'])
list
>>> print(f.__annotations__['c'])
this_cant_evaluate
>>> isinstance(eval(f.__annotations__['d']), stypes)
True
>>> print(f.__annotations__['return'][1:-1]) # First and last could be either " or '
Return me!
>>> f.__annotations__['return'][0] == f.__annotations__['return'][-1]
True
"""
pass
......@@ -197,9 +197,10 @@ class AsyncBadSyntaxTest(unittest.TestCase):
pass
""",
"""async def foo(a:await something()):
pass
""",
#"""async def foo(a:await something()):
# pass
#""", # No longer an error with pep-563 (although still nonsense)
# Some other similar tests have also been commented out
"""async def foo():
def bar():
......@@ -397,9 +398,9 @@ class AsyncBadSyntaxTest(unittest.TestCase):
pass
""",
"""async def foo(a:await b):
pass
""",
#"""async def foo(a:await b):
# pass
#""",
"""def baz():
async def foo(a=await b):
......@@ -612,9 +613,9 @@ class AsyncBadSyntaxTest(unittest.TestCase):
pass
""",
"""async def foo(a:await b):
pass
""",
#"""async def foo(a:await b):
# pass
#""",
"""def baz():
async def foo(a=await b):
......
......@@ -1112,7 +1112,7 @@ class GrammarTests(unittest.TestCase):
check_syntax_error(self, "class foo:yield 1")
check_syntax_error(self, "class foo:yield from ()")
# Check annotation refleak on SyntaxError
check_syntax_error(self, "def g(a:(yield)): pass")
#check_syntax_error(self, "def g(a:(yield)): pass") # no longer a syntax error with PEP563
@skip("DeprecationWarning not implemented")
def test_yield_in_comprehensions(self):
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment